Skip to content

lib: monkey: upgrade to v1.8.9 - #12212

Merged
edsiper merged 2 commits into
masterfrom
lib-monkey-1.8.9
Aug 4, 2026
Merged

lib: monkey: upgrade to v1.8.9#12212
edsiper merged 2 commits into
masterfrom
lib-monkey-1.8.9

Conversation

@edsiper

@edsiper edsiper commented Aug 4, 2026

Copy link
Copy Markdown
Member

Fluent Bit is licensed under Apache 2.0, by submitting this pull request I understand that this code will be released under the terms of that license.

Summary by CodeRabbit

  • New Features

    • Added HTTPS support with OpenSSL and mbedTLS, certificate configuration, and built-in development certificate fallback.
    • Added configurable paths for configuration, logs, PID files, and website content.
    • Improved Windows compatibility and native socket handling.
  • Bug Fixes

    • Strengthened HTTP parsing, validation, security protections, keep-alive, pipelining, and error handling.
  • Documentation

    • Expanded build, configuration, runtime, architecture, and contribution guidance.
  • Tests

    • Added comprehensive HTTP, HTTPS, Windows, and integration-test coverage.

Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d6f693f2-7f73-4be6-a42d-1d7fa7161ba8

📥 Commits

Reviewing files that changed from the base of the PR and between 99462f5 and d8a8535.

📒 Files selected for processing (1)
  • CMakeLists.txt

📝 Walkthrough

Walkthrough

This PR updates the bundled Monkey HTTP server to a newer upstream version. It adds a pluggable TLS transport layer (OpenSSL, mbedTLS, or a disabled stub), refactors network transport dispatch through mk_plugin_network, adds Windows socketpair support, adds a Python integration-test suite, and upgrades the vendored libevent dependency to a release with mbedTLS, WebSocket, and Windows backend support.

Changes

Monkey Server TLS Integration

Layer / File(s) Summary
CI, docs, and root build configuration
.github/workflows/build-pr.yaml, .gitignore, AGENTS.md, CMakeLists.txt, CONTRIBUTING.md, README.md, htdocs/index.html
The CI workflow installs a pinned CMake version and enables mbedTLS for ARM builds. The root build file replaces MK_PLUGIN_TLS with a MK_TLS/MK_TLS_BACKEND option and adds configurable paths. Documentation and branding are updated.
Core headers and TLS transport contract
include/monkey/mk_config.h, include/monkey/mk_net.h, include/monkey/mk_plugin_net.h, include/monkey/mk_server.h, include/monkey/mk_tls_transport.h, other include/monkey/* headers
Server configuration gains TLS fields. mk_plugin_network gains an event_interest callback. A new mk_tls_transport.h header defines the TLS lifecycle API.
Windows socketpair and mk_core support
mk_core/CMakeLists.txt, mk_core/mk_event_libevent.c, mk_core/mk_memory.c, mk_core/mk_string.c, mk_core/mk_win32_socketpair.c
Adds a Windows-specific socketpair implementation and Winsock initialization used by the libevent backend and FIFO channels.
CLI entrypoint TLS options
mk_bin/monkey.c
Adds --https and TLS certificate/key/chain/DH-parameter command-line options and automatic configuration-directory resolution.
TLS backend implementations
tls/*, plugins/tls/*
Adds OpenSSL and mbedTLS TLS network-transport implementations and a disabled stub, selected at build time in tls/CMakeLists.txt.
HTTP server core, scheduler, and network dispatch
mk_server/*, plugins/dirlisting/*
Wires TLS transport selection into mk_server/mk_scheduler, tightens mk_http_parser validation (empty header values, strict Content-Length), and updates event-interest dispatch in mk_net/mk_stream.
Integration and unit tests
integration_tests/*, test/*
Adds a Python-based integration-test suite using a MonkeyManager harness, and unit tests for the default transport and Windows socketpair.

Vendored libevent Library Upgrade

Layer / File(s) Summary
Build system, packaging, and vendor documentation
mk_core/deps/libevent/CMakeLists.txt, Makefile.am, configure.ac, m4/*, cmake/*, build-aux/*
Modernizes the Autotools/CMake build, adds mbedTLS discovery, and updates packaging files.
Changelog and release documentation
ChangeLog*, whatsnew-*.txt, README.md, Documentation/Building.md
Adds release notes for libevent 2.2.x and updated build documentation.
Core event loop, buffers, threading, and platform backends
event.c, buffer.c, bufferevent*.c, evutil.c, epoll.c, poll.c, kqueue.c, wepoll.c, signalfd.c
Reworks the event loop, buffer chain allocation, and adds Windows wepoll and Linux signalfd backends.
DNS, RPC, listener, and HTTP client/server
evdns.c, evrpc.c, listener.c, http.c
Adds TCP/EDNS DNS support, RPC error-handling fixes, and expanded HTTP method/URI/WebSocket-upgrade handling.
OpenSSL/mbedTLS bufferevent backends and WebSocket support
bufferevent_ssl.c, bufferevent_openssl.c, bufferevent_mbedtls.c, ws.c, sha1.c
Introduces a shared SSL bufferevent abstraction and a complete WebSocket implementation.
Sample programs
sample/*
Adds HTTPS client, becat, WebSocket chat, and watch-timing samples.
Regression test suite and tooling scripts
test/*
Adds and extends the libevent regression test suite and test-runner scripts.

Estimated code review effort: 5 (Critical) | ~180+ minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant mk_server
  participant mk_net
  participant mk_tls_transport
  participant TLSBackend as OpenSSL/mbedTLS

  Client->>mk_server: TCP connect
  mk_server->>mk_net: mk_net_transport_default() or mk_tls_transport()
  mk_net->>mk_tls_transport: mk_tls_enabled()
  mk_tls_transport->>TLSBackend: mk_tls_init(server)
  TLSBackend-->>mk_tls_transport: TLS handshake complete
  mk_tls_transport-->>mk_server: read/write via mk_plugin_network callbacks
  mk_server-->>Client: HTTP response
Loading

Possibly related PRs

  • fluent/fluent-bit#10870: Both PRs modify the bundled Monkey library, including lib/monkey/CMakeLists.txt versioning and mk_server/mk_http.c.
  • fluent/fluent-bit#12175: Both PRs update lib/monkey/mk_server/mk_http_parser.c to accept empty HTTP header values, with matching test coverage.
  • fluent/fluent-bit#11644: Both PRs add repository-specific AGENTS.md contributor guidance.

Suggested reviewers: celalettin1286, niedbalski, patrick-stephens

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: upgrading the bundled Monkey library to version 1.8.9.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lib-monkey-1.8.9

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 99462f508d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +688 to +689
else {
ret = SSL_write(ssl, buf, used);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop TLS sendfile at the requested byte count

When a TLS static-file response requests fewer bytes than remain in the file, such as an HTTP range response, remain eventually reaches zero but the next loop iteration enters this branch and writes another full buffer. The response therefore continues through the rest of the file despite its advertised Content-Length, which can corrupt the next response on a keep-alive connection; terminate the loop when remain reaches zero. The equivalent loop in tls_mbedtls.c has the same behavior.

Useful? React with 👍 / 👎.

Comment thread lib/monkey/CMakeLists.txt
option(MK_PLUGIN_LOGGER "Log Writer" No)
option(MK_PLUGIN_MANDRIL "Security" Yes)
option(MK_PLUGIN_TLS "TLS/SSL support" No)
option(MK_TLS "TLS/SSL support" Yes)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Tie Monkey TLS to FLB_TLS

When Fluent Bit is configured with -DFLB_TLS=Off while the HTTP server remains enabled, this independent default still leaves MK_TLS=ON; the Monkey subdirectory then discovers OpenSSL or builds its bundled mbedTLS backend. TLS-disabled Fluent Bit builds therefore retain TLS dependencies and code instead of honoring the top-level option, so this setting should inherit FLB_TLS when Monkey is embedded.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
lib/monkey/mk_core/deps/libevent/evdns.c (1)

89-107: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Document the vendored libevent source clearly.

The tree declares libevent 2.2.2-alpha, and lib/monkey/mk_core/deps/libevent/ChangeLog describes changes in that alpha release. Add a local vendor note that says whether this is the upstream tag or an unreleased snapshot, and clarify the upstream project/version.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/evdns.c` around lines 89 - 107, Add a local
vendor note in the libevent dependency directory documenting the upstream
project and version (libevent 2.2.2-alpha), and explicitly state whether the
vendored source matches an upstream tag or is an unreleased snapshot. Keep the
note alongside the existing ChangeLog and avoid modifying evdns.c behavior.
lib/monkey/mk_core/deps/libevent/test/bench_httpclient.c (1)

161-170: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Free ri in the new bufferevent_socket_new failure path.

ri is allocated at line 161. The new early return at lines 166-170 leaves it unreferenced. Cppcheck reports the same leak at line 169.

Also note perror is not accurate here, because bufferevent_socket_new does not set errno. Consider fprintf(stderr, ...).

🧹 Proposed fix
 	b = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
 	if (b == NULL) {
-		perror("bufferevent_socket_new");
+		fprintf(stderr, "bufferevent_socket_new failed\n");
+		free(ri);
 		evutil_closesocket(sock);
 		return -1;
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/test/bench_httpclient.c` around lines 161 -
170, Update the bufferevent_socket_new failure branch in the benchmark setup to
free the previously allocated ri before returning, and replace perror with an
explicit fprintf(stderr, ...) message because this function does not set errno.

Source: Linters/SAST tools

lib/monkey/mk_core/deps/libevent/test/regress_buffer.c (1)

156-159: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

The trailing-empty-chain loop never advances the cursor.

chain is not reassigned inside the loop body. If buf ends with an empty chain that follows a non-empty chain, evbuffer_get_waste loops forever and test_evbuffer_expand hangs. The current test data reaches this loop only with chain == NULL, so the defect is latent.

This code is upstream libevent, and the repository guidelines require confirmation before editing bundled library code. Confirm whether you want a local patch or prefer to keep the vendor drop byte-identical.

🐛 Proposed fix
 	/* subsequent empty chains */
 	while (chain) {
 		a += chain->buffer_len;
+		chain = chain->next;
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/test/regress_buffer.c` around lines 156 -
159, Confirm whether to patch the bundled libevent code locally or preserve the
vendor drop byte-identical; if patching is approved, update the
trailing-empty-chain loop in evbuffer_get_waste/test_evbuffer_expand to advance
chain on every iteration so it terminates after processing all chains.

Source: Coding guidelines

🟠 Major comments (36)
lib/monkey/integration_tests/run_tests.py-11-13 (1)

11-13: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Select the virtual-environment interpreter for Windows.

VENV_PYTHON always uses .venv/bin/python3. Windows virtual environments use .venv\\Scripts\\python.exe. This prevents the runner from re-executing in its managed environment on Windows.

Proposed fix
 SUITE_ROOT = Path(__file__).resolve().parent
-VENV_PYTHON = SUITE_ROOT / ".venv" / "bin" / "python3"
+if os.name == "nt":
+    VENV_PYTHON = SUITE_ROOT / ".venv" / "Scripts" / "python.exe"
+else:
+    VENV_PYTHON = SUITE_ROOT / ".venv" / "bin" / "python3"
 REEXEC_ENV = "MONKEY_INTEGRATION_REEXEC"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/integration_tests/run_tests.py` around lines 11 - 13, Update the
VENV_PYTHON interpreter selection to use the Windows virtual-environment path
`.venv/Scripts/python.exe` on Windows and retain `.venv/bin/python3` on other
platforms, using the existing SUITE_ROOT and platform-detection facilities.
lib/monkey/mk_core/mk_event_libevent.c-69-79 (1)

69-79: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

) Make Winsock initialization completion-safe.

These two sites publish WSAStartup success before the Winsock layer is actually initialized. Concurrent callers can use sockets while startup is still in progress.

Sites to fix

lib/monkey/mk_core/mk_event_libevent.c#L69 and lib/monkey/mk_core/mk_win32_socketpair.c#L43 both set the initialized flag before WSAStartup() completes.

  • Use a completion-safe one-time initialization primitive such as a three-state flag/lock, or InitOnceExecuteOnce() where available.
  • Hold the same lifecycle rule in mk_win32_socketpair() so direct socketpair callers do not bypass the Winsock completion barrier.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/mk_event_libevent.c` around lines 69 - 79, Make Winsock
initialization completion-safe in lib/monkey/mk_core/mk_event_libevent.c lines
69-79 by replacing the pre-startup initialized flag publication with a one-time
initialization mechanism that blocks concurrent callers until WSAStartup
completes and resets the state on failure. Apply the same lifecycle rule in
lib/monkey/mk_core/mk_win32_socketpair.c lines 43-51 so mk_win32_socketpair()
cannot bypass the completion barrier; both sites must share the same
initialization state and only permit socket use after successful startup.
lib/monkey/test/lib_server.c-244-256 (1)

244-256: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use TEST_ASSERT for the NULL check.

TEST_CHECK records a failure and continues. If mk_net_transport_default() returns NULL, execution reaches line 250 and dereferences a NULL pointer. The test binary then crashes instead of reporting a failure. Use TEST_ASSERT so the test stops at the NULL check.

🛠️ Proposed fix
     transport = mk_net_transport_default();
-    TEST_CHECK(transport != NULL);
+    TEST_ASSERT(transport != NULL);
     TEST_CHECK(transport->read != NULL);

Based on the coding guideline "Validate both success and failure paths, including invalid payloads, boundary sizes, and null or missing fields."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/test/lib_server.c` around lines 244 - 256, Replace the initial
transport NULL check in test_core_plain_transport_available with TEST_ASSERT so
the test stops immediately when mk_net_transport_default() returns NULL; leave
the subsequent transport member checks unchanged.

Source: Coding guidelines

lib/monkey/integration_tests/src/monkey_manager.py-228-245 (1)

228-245: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Valgrind errors are never detected with the current shutdown path.

stop() calls self.process.terminate(), which sends SIGTERM. When Valgrind exits because of a signal, the process exit status reports the signal, not --error-exitcode=99. _validate_valgrind therefore never observes 99, and leak reports are silently ignored.

Parse the Valgrind log for the error summary instead of relying only on the exit code.

🛠️ Proposed approach
     def _validate_valgrind(self, return_code: int) -> None:
         if not self.valgrind_enabled:
             return
 
         if return_code == 99:
             raise AssertionError(self.valgrind_log_file.read_text(encoding="utf-8"))
+
+        if not self.valgrind_log_file.exists():
+            return
+
+        report = self.valgrind_log_file.read_text(encoding="utf-8", errors="replace")
+        match = re.search(r"ERROR SUMMARY: (\d+) errors", report)
+        if match and int(match.group(1)) > 0:
+            raise AssertionError(report)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/integration_tests/src/monkey_manager.py` around lines 228 - 245,
Update _validate_valgrind to inspect the Valgrind log for its error summary and
detect reported errors even when stop() terminates the process via SIGTERM;
retain the existing exit-code check where applicable, and raise AssertionError
with the log contents whenever Valgrind reports errors.
lib/monkey/.github/workflows/build-pr.yaml-83-90 (1)

83-90: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Pin and verify the CMake archive SHA-256 before extraction.

Invoke-WebRequest writes the file, lines 83-90 only check the ZIP PK header, then Expand-Archive extracts it. Use a pinned SHA-256 digest for cmake-${cmakeVersion}-.windows-${cmakeArch}.zip and compare it with Get-FileHash -Algorithm SHA256 before extracting. Do not download the expected digest from the release source at runtime.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/.github/workflows/build-pr.yaml` around lines 83 - 90, Update the
workflow validation between Invoke-WebRequest and Expand-Archive to compare
Get-FileHash -Algorithm SHA256 for $cmakeZip against a pinned,
repository-defined SHA-256 digest for
cmake-${cmakeVersion}-.windows-${cmakeArch}.zip. Fail the workflow on mismatch,
keep the existing ZIP-header validation if appropriate, and do not retrieve the
expected digest from the release source at runtime.
lib/monkey/CMakeLists.txt-64-70 (1)

64-70: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Give MK_TLS precedence over MK_PLUGIN_TLS.

MK_TLS is exposed as an explicit option, but the compatibility block can overwrite it with MK_PLUGIN_TLS after option() has already defined it. Assign from MK_PLUGIN_TLS only when MK_TLS is not already defined, or remove the later override.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/CMakeLists.txt` around lines 64 - 70, Update the compatibility
block around MK_TLS and MK_PLUGIN_TLS so the explicit MK_TLS option always takes
precedence. Only assign MK_TLS from MK_PLUGIN_TLS when MK_TLS has not already
been defined, or remove the override while preserving legacy compatibility.
lib/monkey/mk_core/deps/libevent/CMakeLists.txt-131-137 (1)

131-137: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align EVENT__DISABLE_OPENSSL in lib/monkey/mk_core/deps/CMakeLists.txt with the new string option.

lib/monkey/mk_core/deps/CMakeLists.txt forces EVENT__DISABLE_OPENSSL as a BOOL value (ON), so CMake treats it as ON/OFF at the caller: when Monkey does not explicitly pass AUTO, libevent sees ON, disables OpenSSL, and stops adding the bundled SSL backend. Set the caller and documentation to the accepted values AUTO, ON, or OFF instead, so future callers do not generate invalid bools.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/CMakeLists.txt` around lines 131 - 137,
Update the EVENT__DISABLE_OPENSSL definition in the parent CMake configuration
and its documentation to use the string values AUTO, ON, or OFF instead of BOOL
semantics or a forced ON value. Preserve the libevent option’s accepted-value
behavior and ensure callers default to AUTO unless explicitly selecting ON or
OFF.
lib/monkey/mk_core/deps/libevent/make-event-config.sed-27-27 (1)

27-27: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use \3 for the symbol letter in the ifdef/ifndef rule.

This bundled lib/monkey/mk_core/deps/libevent/make-event-config.sed rule captures indent without renaming STDC_HEADERS, but the ifdef/ifndef symbol uses the wrong group. For input #ifdef FOO, the generated guard becomes #ifdef EVENT__OO; for #ifndef FOO, it becomes #ifndef EVENT__nOO, while the define/undef entries use the renamed symbol name.

🐛 Proposed fix for the capture-group index
-s/#\( *\)if\(n*\)def \([A-Z]\)/#\1if\2def EVENT__\2/
+s/#\( *\)if\(n*\)def \([A-Z]\)/#\1if\2def EVENT__\3/
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/make-event-config.sed` at line 27, Update
the ifdef/ifndef substitution rule in make-event-config.sed to use capture group
\3 for the symbol letter when constructing the EVENT__-prefixed guard,
preserving the existing directive type and indentation captures so FOO becomes
EVENT__FOO consistently with define/undef entries.
lib/monkey/mk_core/deps/libevent/evdns.c-5416-5421 (1)

5416-5421: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unchecked allocations create NULL-dereference paths in the new cache and TCP code.

mm_calloc at Line 5417 is not checked before cache->base, cache->name, and cache->ai are assigned. Static analysis reports the same finding. Two sibling sites share this pattern:

  • Line 5419: mm_strdup(nodename) result is stored in cache->name without a check. evdns_cache_compare calls strcasecmp(a->name, b->name) on that value.
  • Line 1351: reply.data.raw = mm_malloc(buf_size) is not checked before memcpy writes at Line 1378 and Line 1414.
  • Line 5829: data->nodename = mm_strdup(nodename) is not checked before evdns_cache_write uses it.

These are allocation-failure paths only, so normal operation is unaffected. This file is a vendored upstream import, so do not patch it locally. Report the defect to the libevent project and track the fix through the next vendor drop.

As per path instructions, "Ask for explicit user confirmation before editing bundled or separately maintained library code under lib/; keep such patches isolated and document their upstream project/path."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/evdns.c` around lines 5416 - 5421, Do not
modify this vendored libevent file. Report the unchecked allocation defects to
the libevent project, covering the cache allocation and name duplication near
the evdns cache insertion, reply buffer allocation in the TCP code, and nodename
duplication before evdns_cache_write; track the upstream fix for inclusion in
the next vendor drop, and request explicit user confirmation before any
bundled-library edit.

Sources: Path instructions, Linters/SAST tools

lib/monkey/mk_core/deps/libevent/bufferevent.c-885-903 (1)

885-903: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle replacement with the current descriptor.

If fd == old_fd, Line 897 closes the active descriptor. be_async_ctrl() then accepts the same descriptor without reassigning it. The function can return success while the bufferevent retains a closed descriptor.

Return success before closing when both descriptors are equal. Also preserve the current descriptor if BEV_CTRL_SET_FD fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/bufferevent.c` around lines 885 - 903,
Update bufferevent_replacefd to return success without closing or reassigning
when the requested fd matches old_fd, and retain the existing descriptor if
BEV_CTRL_SET_FD fails after closing the old descriptor. Ensure failure handling
restores or preserves the current descriptor state rather than leaving the
bufferevent with a closed descriptor.
lib/monkey/mk_core/deps/libevent/arc4random.c-378-379 (1)

378-379: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Fail closed when entropy seeding fails.

arc4_stir() returns -1 when all entropy sources fail. arc4_stir_if_needed() ignores that result, so arc4random(), arc4random_buf(), and arc4random_uniform() can still call arc4_getword() with an unreseeded RC4 state. Propagate arc4_stir() failure to a fail-closed path, and add failure-injection coverage for each configured entropy seed source.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/arc4random.c` around lines 378 - 379, Update
arc4_stir_if_needed() to check the return value of arc4_stir() and propagate
failure so arc4random(), arc4random_buf(), and arc4random_uniform() never use an
unreseeded RC4 state. Add failure-injection tests covering each configured
entropy seed source and verify all affected APIs fail closed when seeding fails.

Source: Learnings

lib/monkey/mk_core/deps/libevent/bufferevent_async.c-112-123 (1)

112-123: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not accept ERROR_INVALID_PARAMETER as a successful IOCP association.

CreateIoCompletionPort() returns ERROR_INVALID_PARAMETER when a handle is already associated with another IOCP. This helper only checks the result and callers continue as if this IOCP owns the descriptor, so callbacks can queue completions to another port.

Make event_iocp_port_associate_() reject association unless it can confirm the handle already belongs to the same port->port; then both bufferevent_async_new_() and BEV_CTRL_SET_FD fail the path. Add a two-IOCP regression case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/bufferevent_async.c` around lines 112 - 123,
Update fatal_error and event_iocp_port_associate_ so ERROR_INVALID_PARAMETER is
not treated as successful association; only accept an already-associated handle
after confirming it belongs to the same port->port, otherwise reject it. Ensure
bufferevent_async_new_() and BEV_CTRL_SET_FD propagate the failure, and add a
regression test using two IOCP instances.
lib/monkey/mk_core/deps/libevent/configure-13755-13755 (1)

13755-13755: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fix the malformed shell assignment before the Emscripten archive_cmds block.

lib/monkey/mk_core/deps/libevent/configure line 13755 contains ='-fPIC', which is not a valid shell assignment. Use the pic compiler variable assignment, for example lt_prog_compiler_pic='-fPIC', so Emscripten configs do not fail with =-fPIC: command not found before archive_cmds is set.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/configure` at line 13755, Replace the
malformed shell assignment immediately before the Emscripten archive_cmds block
with a valid assignment to the pic compiler variable, using lt_prog_compiler_pic
and retaining the -fPIC value so configure no longer attempts to execute =-fPIC.
lib/monkey/mk_core/deps/libevent/poll.c-106-107 (1)

106-107: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Apply the required C block and declaration conventions.

Use braces for every changed if body. Declare sa with the other sigfd_del local variables.

  • lib/monkey/mk_core/deps/libevent/poll.c#L106-L107: wrap the evsig_init_() fallback in braces.
  • lib/monkey/mk_core/deps/libevent/select.c#L122-L123: wrap the evsig_init_() fallback in braces.
  • lib/monkey/mk_core/deps/libevent/signal.c#L250-L251: wrap the failure return in braces.
  • lib/monkey/mk_core/deps/libevent/signalfd.c#L147-L155: wrap the allocation and registration failure branches in braces.
  • lib/monkey/mk_core/deps/libevent/signalfd.c#L190-L199: move sa to the start of sigfd_del.
  • lib/monkey/mk_core/deps/libevent/signalfd.c#L209-L211: wrap the disabled-backend return in braces.

As per coding guidelines, “Always use braces for if, else, while, and do blocks” and declare variables at the start of functions. Based on learnings, use K&R braces for control statements.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/poll.c` around lines 106 - 107, Apply
K&R-style braces to all specified control-flow branches: wrap the evsig_init_
fallback in poll.c and select.c, the failure return in signal.c, and the
allocation, registration-failure, and disabled-backend branches in signalfd.c;
in signalfd.c, declare sa with the other locals at the start of sigfd_del.
Update every listed file and preserve the existing branch behavior.

Sources: Coding guidelines, Learnings

lib/monkey/mk_core/deps/libevent/signalfd.c-97-127 (1)

97-127: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Preserve the saved handler during reinitialization.

When old is nonzero, sigfd_free_sigevent() frees only sigev. Lines 119-127 then overwrite the existing sig->sh_old[signo] pointer. Each post-fork reinitialization leaks one saved struct sigaction.

Retain the existing saved action during reinitialization. Allocate and query sigaction only for the first registration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/signalfd.c` around lines 97 - 127, Update
the signal registration flow around sigfd_free_sigevent() and sig->sh_old[signo]
so reinitialization with old nonzero preserves the existing saved struct
sigaction. Only allocate sig->sh_old[signo] and query sigaction for the first
registration, while retaining the existing error handling for initial allocation
or sigaction failure.
lib/monkey/mk_core/deps/libevent/signal.c-412-430 (1)

412-430: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not log from evsig_handler.

event_warnx and strerror are not async-signal-safe. A full notification pipe can therefore deadlock or crash while handling a signal. Retry EINTR, but silently drop EAGAIN and other write failures, or record them through an async-signal-safe mechanism.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/signal.c` around lines 412 - 430, Remove the
event_warnx and strerror logging from the write-failure path in evsig_handler.
Continue retrying write when errno is EINTR, and silently ignore EAGAIN and all
other failures while preserving the existing loop termination behavior; do not
invoke non-async-signal-safe functions from the signal handler.
lib/monkey/mk_core/deps/libevent/signalfd.c-53-65 (1)

53-65: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle failed nonblocking signalfd reads.

sigfd_cb calls nonblocking read() via sigfdops = { "signalfd_signal", NULL, ... } < 0. If the descriptor’s readiness becomes stale, read() returns -1 with EAGAIN; if interrupted before any bytes are read, it can return -1 with EINTR. The current EVUTIL_ASSERT(ret == sizeof(fdsi)) turns these recoverable states into debug aborts. Skip EAGAIN/EINTR from the callback path and only handle other non-retryable read() failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/signalfd.c` around lines 53 - 65, Update
sigfd_cb to handle read() failures before asserting the full signalfd_siginfo
size: return quietly for EAGAIN and EINTR, while preserving assertion or
handling for other non-retryable errors. Continue processing fdsi only after a
successful read of sizeof(fdsi).
lib/monkey/mk_core/deps/libevent/signalfd.c-130-137 (1)

130-137: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Set the process-wide signal mask before enabling signalfd.

sigprocmask() only blocks the signal in the calling thread. If another worker thread has no matching block on this signal, a process-directed signal can bypass signalfd and follow the normal disposition or custom handler that libevent saves. Block the target signals before pthread_create()/worker-thread setup, or reject EVENT_USE_SIGNALFD after threaded operation starts. Add a multi-threaded signalfd delivery test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/signalfd.c` around lines 130 - 137, Update
the signalfd setup around the signal-mask logic in signalfd.c so target signals
are blocked process-wide before pthread_create or other worker-thread
initialization, ensuring all threads inherit the mask; alternatively reject
EVENT_USE_SIGNALFD once threaded operation has begun. Preserve existing cleanup
behavior and add a multi-threaded test verifying signals are delivered through
signalfd rather than normal dispositions or handlers.
lib/monkey/mk_core/deps/libevent/wepoll.c-43-69 (1)

43-69: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align EPOLLONESHOT in wepoll.h with wepoll.c.

wepoll.c defines EPOLLONESHOT as bit 31 and checks that bit in sock_feed_event, but wepoll.h defines it as bit 30. The local epoll.c translation unit includes wepoll.h, so the library API contract is inconsistent. Set EPOLLONESHOT to bit 31 in wepoll.h, or centralize it under one header.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/wepoll.c` around lines 43 - 69, Update the
EPOLLONESHOT definition in wepoll.h to use bit 31, matching the enum and macro
definitions in wepoll.c and the bit checked by sock_feed_event. Keep the public
epoll event constants consistent across both headers and translation units.
lib/monkey/mk_core/deps/libevent/sample/http-server.c-498-520 (1)

498-520: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

main continues after event_base_new_with_config or evhttp_new fails, which causes a null-pointer dereference.

At Line 499 the code sets ret = 1 but does not jump to err. Execution reaches Line 507 and calls evhttp_new(NULL). At Line 508 the code again sets ret = 1 without jumping to err. Execution reaches Line 514 and calls evhttp_set_cb(http, ...) with http == NULL. The sample crashes instead of exiting with an error.

Related error-path gaps in the same function share this cause:

  • Line 535 uses return 1 instead of ret = 1; goto err;, so http, base, and cfg leak.
  • Lines 577 and 579 jump to err without setting ret, so the sample returns success after a failure.

This file is vendored upstream libevent sample code. Do not patch it inline as part of this upgrade. Report the defect upstream, or isolate any local fix in a separate commit that documents the upstream project and path.

🐛 Reference fix, if a local patch is approved
 	base = event_base_new_with_config(cfg);
 	if (!base) {
 		fprintf(stderr, "Couldn't create an event_base: exiting\n");
 		ret = 1;
+		goto err;
 	}
 	event_config_free(cfg);
 	cfg = NULL;
 
 	/* Create a new evhttp object to handle requests. */
 	http = evhttp_new(base);
 	if (!http) {
 		fprintf(stderr, "couldn't create evhttp. Exiting.\n");
 		ret = 1;
+		goto err;
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/sample/http-server.c` around lines 498 -
520, Do not modify the vendored sample inline. Report the error-path defect
upstream, or create a separate documented commit for the local fix identifying
upstream libevent and this sample path; if approved, ensure main exits via the
existing err cleanup path after event_base_new_with_config or evhttp_new fails,
replaces the direct return failure path with ret = 1 followed by goto err, and
sets ret before the later err jumps so failures cannot return success.

Source: Coding guidelines

lib/monkey/mk_core/deps/libevent/test/regress_ws.c-110-113 (1)

110-113: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

event_base_loopexit receives 0xDEADBEEF, not an event base.

http_on_ws_cb at line 134 passes (void *)0xDEADBEEF as the callback argument. In on_ws_msg_cb, arg therefore holds 0xDEADBEEF. Line 112 passes that value to event_base_loopexit as a struct event_base *.

If an unexpected message arrives, the process dereferences an invalid pointer instead of exiting the loop. The file-static exit_base holds the correct base and is already used for that purpose at line 444.

Use exit_base, and set test_ok to a failure value so the cause is visible.

🐛 Proposed fix
 	} else {
 		/* unexpected test message */
-		event_base_loopexit(arg, NULL);
+		test_ok = -1;
+		event_base_loopexit(exit_base, NULL);
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/test/regress_ws.c` around lines 110 - 113,
Update the unexpected-message branch in on_ws_msg_cb to call event_base_loopexit
with the file-static exit_base instead of the callback arg, and set test_ok to
the existing failure value before exiting so the test records the unexpected
message.
lib/monkey/mk_core/deps/libevent/test/regress_ssl.c-324-327 (1)

324-327: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

if (REGRESS_OPENSSL_BATCH_WRITE) is always true.

The condition tests the enum constant REGRESS_OPENSSL_BATCH_WRITE (value 8192), not the type bitmask. BUFFEREVENT_SSL_BATCH_WRITE is therefore set on every bufferevent in every testcase that calls open_ssl_bufevs.

Two effects follow. The bufferevent_socketpair_batch_write testcase no longer tests a distinct configuration. All other testcases silently run in batch-write mode, so the non-batch write path loses coverage.

Test the bit in type.

This is vendored upstream code. Confirm the upstream release carries the same defect before you patch it locally, and prefer reporting it upstream.

🐛 Proposed fix
-	if (REGRESS_OPENSSL_BATCH_WRITE) {
+	if (type & REGRESS_OPENSSL_BATCH_WRITE) {
 		bufferevent_ssl_set_flags(*bev1_out, BUFFEREVENT_SSL_BATCH_WRITE);
 		bufferevent_ssl_set_flags(*bev2_out, BUFFEREVENT_SSL_BATCH_WRITE);
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/test/regress_ssl.c` around lines 324 - 327,
Update the condition in open_ssl_bufevs to test whether the type bitmask
includes REGRESS_OPENSSL_BATCH_WRITE, rather than testing the enum constant
directly; keep BUFFEREVENT_SSL_BATCH_WRITE enabled only for batch-write
configurations. Before applying the local patch, verify the vendored upstream
release contains the same defect and prefer reporting it upstream.
lib/monkey/mk_core/deps/libevent/test/regress_testutils.c-396-420 (1)

396-420: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

parse_csv_address_list ignores family and can dereference a NULL token.

Two defects exist in this function.

First, line 412 passes AF_INET to evutil_inet_pton instead of family. The function computes next_addr as a struct in6_addr * when family is AF_INET6, but then parses the token as IPv4. IPv6 input fails to parse, and a successful IPv4 parse writes only 4 bytes into a 16-byte slot. The AAAA branch at lines 255-261 does not call this helper today, so the defect is latent, but it will trigger for any future IPv6 caller.

Second, the loop is a do/while. If s contains no token, strtok returns NULL at line 407 and the body still runs, so evutil_inet_pton receives a NULL string pointer.

Use family in the parse call and convert the loop to a while.

This is vendored upstream code. Confirm the upstream release before you diverge locally.

🐛 Proposed fix
 	token = strtok(buf, ",");
-	do {
+	while (token) {
 		tt_assert((unsigned)i < addrs_size);
 		next_addr = (family == AF_INET) ? (void *)((struct in_addr*)addrs + i)
 			: (void *)((struct in6_addr*)addrs + i);
-		if (!evutil_inet_pton(AF_INET, token, next_addr)) {
+		if (!evutil_inet_pton(family, token, next_addr)) {
 			TT_DIE(("Bad %s value %s in table", (family == AF_INET) ? "A" :"AAAA", token));
 		}
 		++i;
 		token = strtok (NULL, ",");
-	} while (token);
+	}
 end:
 	return i;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/test/regress_testutils.c` around lines 396 -
420, Update parse_csv_address_list to pass its family parameter to
evutil_inet_pton, preserving correct IPv4 and IPv6 parsing for the address
storage selected by family. Replace the do/while iteration with a while loop so
the body executes only when strtok returns a non-NULL token, and confirm the
corresponding upstream vendored release before applying the local change.
lib/monkey/mk_core/deps/libevent/test/test.sh-144-149 (1)

144-149: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

do_test never applies the backend configuration.

Line 144 shifts the positional parameters. For the call do_test EPOLL "(timerfd)", $# is 1 after the shift, so the test [ $# -gt 1 ] at line 145 is false. backend_conf stays empty. The timerfd, changelist, timerfd+changelist, and signalfd branches at lines 153-162 never run, so the -t, -c, -T, and -S options silently run plain backend tests.

Line 146 also reads $2 after the shift, which is the parameter after the configuration.

🐛 Proposed fix
 do_test() {
 	backend="$1" && shift
-	if [ $# -gt 1 ]; then
-		backend_conf="$2" && shift
+	if [ $# -gt 0 ]; then
+		backend_conf="$1" && shift
 	else
 		backend_conf=""
 	fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/test/test.sh` around lines 144 - 149, Fix
do_test’s positional-argument handling so the optional backend configuration is
captured before shifting away the backend argument. Detect the configuration
based on the remaining argument count and read the correct positional parameter,
ensuring calls such as do_test EPOLL "(timerfd)" set backend_conf and activate
the -t, -c, -T, and -S test branches.
lib/monkey/tls/tls.conf-22-23 (1)

22-23: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Generate at least 2048-bit DH parameters.

Line 23 recommends 1024-bit DH parameters. This strength is obsolete and can fail stricter TLS security policies. Change the sample command to generate 2048-bit parameters.

Proposed fix
-    # $ openssl dhparam -out dhparam.pem 1024
+    # $ openssl dhparam -out dhparam.pem 2048
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/tls/tls.conf` around lines 22 - 23, Update the sample OpenSSL
command in the TLS configuration comment to generate 2048-bit DH parameters
instead of 1024-bit parameters.
lib/monkey/tls/tls_openssl.c-677-699 (1)

677-699: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

mk_tls_send_file sends data past file_count in both TLS backends. The same loop was copied into both files. remain starts at file_count and decreases with each write, but the loop only exits when a write fails or the file ends. Once remain reaches 0, the remain > 0 branch is skipped and the else branch writes the full buffer, so the transport keeps sending until end of file. Range requests and chunk boundaries break as a result.

  • lib/monkey/tls/tls_openssl.c#L677-L699: exit the do/while loop when remain reaches 0, and limit each tls_pread to min(SENDFILE_BUF_SIZE, remain).
  • lib/monkey/tls/tls_mbedtls.c#L779-L802: apply the identical change to the mbedTLS loop, keeping handle_return for the error path.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/tls/tls_openssl.c` around lines 677 - 699, Update the
mk_tls_send_file loop in lib/monkey/tls/tls_openssl.c:677-699 to stop when
remain reaches zero and pass min(SENDFILE_BUF_SIZE, remain) to tls_pread; apply
the identical bounds and termination change in
lib/monkey/tls/tls_mbedtls.c:779-802 while preserving handle_return on the
mbedTLS error path.
lib/monkey/mk_bin/monkey.c-356-364 (1)

356-364: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle a failed configuration-directory resolution.

mk_default_config_dir returns NULL when no candidate directory exists. Line 363 then assigns NULL to server->path_conf_root. mk_server_setup calls mk_config_start_configure(server) with that NULL root, and the guard added at lib/monkey/mk_server/monkey.c line 195 only triggers when path_conf_root is not NULL. The failure is therefore silent, and the reported error does not name the real cause.

🛡️ Proposed fix
     else {
         resolved_config_dir = mk_default_config_dir(default_config_dir,
                                                     sizeof(default_config_dir),
                                                     argv[0]);
+        if (resolved_config_dir == NULL) {
+            mk_err("Could not locate a configuration directory, use -c/--configdir.");
+            return EXIT_FAILURE;
+        }
         server->path_conf_root = (char *) resolved_config_dir;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_bin/monkey.c` around lines 356 - 364, Handle a NULL result from
mk_default_config_dir before assigning server->path_conf_root: detect the failed
resolution, report a configuration-directory resolution error that identifies
the actual cause, and abort or propagate failure through the surrounding server
setup path instead of continuing with a NULL root. Preserve the existing
path_config and successful default-directory branches.
lib/monkey/tls/tls_mbedtls.c-854-880 (1)

854-880: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Initialize server_context->mutex before use.

mk_mem_alloc_z only zeroes the memory. tls_global_init then locks server_context->mutex at line 503, and mk_tls_exit calls pthread_mutex_destroy on it at line 962. A zeroed pthread_mutex_t is not a portable initialized mutex; on Windows and on some pthread implementations the lock and the destroy are undefined behavior. The OpenSSL backend calls pthread_mutex_init at lib/monkey/tls/tls_openssl.c line 768.

mk_tls_init also does not check the allocation result before writing server_context->server.

🛡️ Proposed fix
     if (used) {
         /* If it's used, load certificates.. mandatory */
         server_context = mk_mem_alloc_z(sizeof(struct polar_server_context));
+        if (server_context == NULL) {
+            return -1;
+        }
+        pthread_mutex_init(&server_context->mutex, NULL);
         server_context->server = server;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/tls/tls_mbedtls.c` around lines 854 - 880, Update mk_tls_init to
validate the mk_mem_alloc_z result before dereferencing server_context, and
initialize server_context->mutex with pthread_mutex_init before calling
tls_global_init. Handle allocation or mutex-initialization failure through the
existing cleanup/error path so mk_tls_exit never destroys an uninitialized
mutex.
lib/monkey/mk_bin/monkey.c-141-159 (1)

141-159: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

The resolver prefers a working-directory-relative conf over the installed configuration.

Line 141 checks the relative path conf before the compiled-in MK_PATH_CONF. The server then loads configuration, MIME types, sites, and plugin definitions from whatever directory the process was started in. If the working directory is writable by another user, that user controls the server configuration, including listeners and TLS file paths.

Check MK_PATH_CONF first, and treat the relative conf directory as a development fallback only.

realpath(path, buf) at line 142 also requires buf to hold at least PATH_MAX bytes; it ignores the size parameter. The current caller passes a PATH_MAX buffer, so no overflow occurs today. Add a guard so a future caller with a smaller buffer cannot overflow. The same applies at lines 183 and 199.

🔒️ Proposed reordering
 static const char *mk_default_config_dir(char *buf, size_t size, const char *argv0)
 {
     char exe_path[PATH_MAX];
     char candidate[PATH_MAX];
     char *last_slash;
     int written;
     ssize_t len;
 
+    if (size < PATH_MAX) {
+        return NULL;
+    }
+
+    if (MK_PATH_CONF[0] != '\0' && mk_dir_exists(MK_PATH_CONF) == MK_TRUE) {
+        written = snprintf(buf, size, "%s", MK_PATH_CONF);
+        if (written < 0 || (size_t) written >= size) {
+            return NULL;
+        }
+        return buf;
+    }
+
     if (mk_dir_exists("conf") == MK_TRUE) {
         if (realpath("conf", buf) != NULL) {
             return buf;
         }
 
         written = snprintf(buf, size, "%s", "conf");
         if (written < 0 || (size_t) written >= size) {
             return NULL;
         }
         return buf;
     }
-
-    if (MK_PATH_CONF[0] != '\0' && mk_dir_exists(MK_PATH_CONF) == MK_TRUE) {
-        written = snprintf(buf, size, "%s", MK_PATH_CONF);
-        if (written < 0 || (size_t) written >= size) {
-            return NULL;
-        }
-        return buf;
-    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_bin/monkey.c` around lines 141 - 159, Update the
configuration-directory resolver to check the compiled-in MK_PATH_CONF location
before the working-directory-relative conf fallback, ensuring installed
configuration takes precedence. In the relative conf branch, validate that the
supplied buffer size is at least PATH_MAX before calling realpath, and apply the
same guard to the realpath calls at the other referenced locations. Preserve the
existing fallback and failure behavior for invalid or insufficient paths.
lib/monkey/plugins/dirlisting/dirlisting.c-811-828 (1)

811-828: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Deallocate the dirlisting request on HTML-title allocation failures.

After request->file_list is allocated, failures in mk_string_html_escape() and the first mk_dirhtml_tag_assign() currently mk_api->mem_free(request) directly. This skips mk_dirhtml_cleanup() logic, leaving sr->handler_data pointing at freed request state and leaking request->file_list plus request->toc. Clear both paths like mk_http_cb_request_free() clears sr->handler_data, and free request->file_list/request->toc; otherwise keep the request lifecycle through mk_dirhtml_cleanup().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/plugins/dirlisting/dirlisting.c` around lines 811 - 828, Update
the failure paths after request->file_list allocation, including
mk_string_html_escape() and the first mk_dirhtml_tag_assign() failure, to
release the request through mk_dirhtml_cleanup(). Ensure cleanup clears
sr->handler_data and frees request->file_list and request->toc before releasing
the request, matching mk_http_cb_request_free() lifecycle behavior.
lib/monkey/mk_server/mk_http.c-1678-1681 (1)

1678-1681: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Confirm the polarity of this mk_list_is_empty check.

mk_list_is_empty() returns 0 for an empty list and -1 for a populated list. The stated intent is to run the stage-40 hook with sr_fixed when the request list is empty. != 0 selects the populated case, which is the opposite. It also runs the hook with sr_fixed while real requests exist in the list.

Use == 0 if the intent is the empty-list case.

Based on learnings: mk_list_is_empty() returns 0 for an empty list and -1 for a populated list.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_server/mk_http.c` around lines 1678 - 1681, Update the
mk_list_is_empty check surrounding mk_plugin_stage_run_40 so it uses the
empty-list return value, selecting session->sr_fixed only when request_list is
empty; preserve the existing stage-40 invocation and populated-list behavior.

Source: Learnings

lib/monkey/plugins/tls/tls_openssl.c-106-172 (1)

106-172: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the configuration directory separator handling consistent.

Line 106, Line 134, Line 155, and Line 165 concatenate confdir and the file name without a separator ("%stls.conf"). Line 140, Line 150, and Line 171 insert a separator ("%s/%s"). Only one of these two assumptions about a trailing slash in confdir can be correct. If confdir has no trailing slash, the fallback paths resolve to the wrong location. If confdir has a trailing slash, the relative-key paths produce a double slash.

Normalize confdir once, then use one format for all paths.

🐛 Proposed direction
 static int config_parse(const char *confdir, struct tls_config *conf)
 {
     long unsigned int len;
     char *conf_path = NULL;
+    const char *sep;
     ...
-    mk_api->str_build(&conf_path, &len, "%stls.conf", confdir);
+    sep = (confdir[0] != '\0' &&
+           confdir[strlen(confdir) - 1] == '/') ? "" : "/";
+    mk_api->str_build(&conf_path, &len, "%s%stls.conf", confdir, sep);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/plugins/tls/tls_openssl.c` around lines 106 - 172, Normalize
confdir once before building any paths, ensuring it has exactly one directory
separator at the boundary. Update the tls.conf path and all fallback assignments
in the surrounding configuration-loading flow, including cert_file,
cert_chain_file, key_file, and dh_param_file, to use the same normalized path
format without producing missing or duplicate separators.
lib/monkey/plugins/tls/tls_openssl.c-396-411 (1)

396-411: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Send only the bytes that were copied.

Line 408 writes len bytes, but the loop at Line 403 copies used bytes. len comes from mk_io->total_len, and used is the sum of the first iov_idx entries. If these two values ever differ, SSL_write transmits uninitialized heap memory to the client.

Use used as the write length, and bound the copy by len.

🔒 Proposed fix
     used = 0;
     for (i = 0; i < mk_io->iov_idx; i++) {
+        if (used + mk_io->io[i].iov_len > len) {
+            break;
+        }
         memcpy(buf + used, mk_io->io[i].iov_base, mk_io->io[i].iov_len);
         used += mk_io->io[i].iov_len;
     }
 
-    ret = SSL_write(ssl, buf, len);
+    ret = SSL_write(ssl, buf, used);
     mk_api->mem_free(buf);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/plugins/tls/tls_openssl.c` around lines 396 - 411, Update the TLS
write logic around the iovec-copy loop and SSL_write call to copy no more than
the allocated len bytes, ensuring used cannot exceed len, and pass used—not
len—as the SSL_write length so only initialized copied bytes are transmitted.
lib/monkey/plugins/tls/tls_openssl.c-567-571 (1)

567-571: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use the plugin API allocator and unlink the list node.

Line 570 calls mk_mem_free directly. Every other allocation in this file uses mk_api->mem_free. A shared plugin object must resolve allocator symbols through mk_api. The loop also does not call mk_list_del before it frees the node.

♻️ Proposed fix
     mk_list_foreach_safe(cur, tmp, &server_context->threads) {
         thctx = mk_list_entry(cur, struct tls_thread_context, _head);
         contexts_free(thctx->contexts);
-        mk_mem_free(thctx);
+        mk_list_del(&thctx->_head);
+        mk_api->mem_free(thctx);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/plugins/tls/tls_openssl.c` around lines 567 - 571, Update the
cleanup loop in the thread-context teardown to unlink each node with mk_list_del
before releasing it, and replace direct mk_mem_free with the plugin allocator
mk_api->mem_free. Preserve the existing contexts_free cleanup and safe iteration
behavior.
lib/monkey/mk_server/mk_http_parser.c-326-337 (1)

326-337: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reject Content-Length when Transfer-Encoding: chunked is present.

The parser now accepts Content-Length and Transfer-Encoding values independently, and mk_http_parser_content_length() returns the chunked body size when chunked encoding is present. RFC 9112 section 6.1 requires rejecting requests with both headers to avoid request-smuggling through different message-length expectations.

Also reject duplicate Content-Length headers instead of letting the later value overwrite the previous one.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_server/mk_http_parser.c` around lines 326 - 337, Update the
Content-Length handling in mk_http_parser_content_length() to return
MK_CLIENT_BAD_REQUEST when Transfer-Encoding: chunked is already present, and
reject a second Content-Length header instead of overwriting
p->header_content_length. Preserve the existing empty-value and parsing-error
checks for the first Content-Length.
lib/monkey/plugins/tls/tls_openssl.c-225-233 (1)

225-233: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use the OpenSSL 3 temporary DH API.

PEM_read_bio_DHparams, SSL_CTX_set_tmp_dh, and DH_free use the deprecated DH/PEM DH APIs. Use OpenSSL 3-compatible parameter loading, such as PEM_read_bio_Parameters_ex, and SSL_CTX_set0_tmp_dh_pkey, with a compatible fallback branch if you still support OpenSSL 1.1.x. Also remove the redundant SSL_load_error_strings() after OPENSSL_init_ssl().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/plugins/tls/tls_openssl.c` around lines 225 - 233, The temporary
DH setup in the TLS initialization flow must use OpenSSL 3 APIs: replace
PEM_read_bio_DHparams, SSL_CTX_set_tmp_dh, and DH_free with parameter loading
via PEM_read_bio_Parameters_ex and SSL_CTX_set0_tmp_dh_pkey, while retaining an
OpenSSL 1.1.x fallback if supported. Also remove the redundant
SSL_load_error_strings call following OPENSSL_init_ssl.

Comment on lines +2182 to 2196
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (activate)
event_active_nolock_(&eonce->ev, EV_TIMEOUT, 1);
else
res = event_add_nolock_(&eonce->ev, tv, 0);

if (res != 0) {
mm_free(eonce);
return (res);
} else {
LIST_INSERT_HEAD(&base->once_events, eonce, next_once);
}
EVBASE_RELEASE_LOCK(base, th_base_lock);

return (0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Release the base lock on the event_add_nolock_ failure path.

Line 2182 acquires th_base_lock. If event_add_nolock_ returns non-zero, the function frees eonce and returns at line 2190 while the lock is still held. The base then stays locked forever, and any later operation on that base deadlocks. Fix the early return to release the lock.

🔒️ Proposed fix
 	if (res != 0) {
 		mm_free(eonce);
+		EVBASE_RELEASE_LOCK(base, th_base_lock);
 		return (res);
 	} else {
 		LIST_INSERT_HEAD(&base->once_events, eonce, next_once);
 	}
 	EVBASE_RELEASE_LOCK(base, th_base_lock);

This code is vendored under lib/. Confirm the change against upstream libevent before you apply it, and keep the patch isolated with a note about the upstream project and path.

As per coding guidelines: "Ask for explicit user confirmation before editing bundled or separately maintained library code under lib/; keep such patches isolated and document their upstream project/path."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (activate)
event_active_nolock_(&eonce->ev, EV_TIMEOUT, 1);
else
res = event_add_nolock_(&eonce->ev, tv, 0);
if (res != 0) {
mm_free(eonce);
return (res);
} else {
LIST_INSERT_HEAD(&base->once_events, eonce, next_once);
}
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (0);
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (activate)
event_active_nolock_(&eonce->ev, EV_TIMEOUT, 1);
else
res = event_add_nolock_(&eonce->ev, tv, 0);
if (res != 0) {
mm_free(eonce);
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (res);
} else {
LIST_INSERT_HEAD(&base->once_events, eonce, next_once);
}
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (0);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/event.c` around lines 2182 - 2196, In the
event_once scheduling flow around event_add_nolock_, release th_base_lock via
EVBASE_RELEASE_LOCK(base, th_base_lock) before freeing eonce and returning on a
nonzero result. Confirm the fix against upstream libevent first, obtain explicit
user confirmation before modifying this vendored library code, and keep the
isolated patch documented with its upstream project and path.

Source: Coding guidelines

Comment on lines +1076 to +1083
err:
evrpc_hook_context_free_(store);
if (meta) {
mm_free(meta->data);
mm_free(meta->key);
mm_free(meta);
}
return 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

The err: path leaves req->hook_meta dangling and can dereference NULL.

store aliases req->hook_meta (Line 1055-1056). The error path frees store through evrpc_hook_context_free_ but never clears req->hook_meta. Two consequences follow:

  • evrpc_hook_find_meta (Line 1092) and evrpc_hook_get_connection (Line 1110) then read freed memory. test/regress_rpc.c calls evrpc_hook_add_meta and evrpc_hook_get_connection in exactly that order.
  • If store already held metadata from an earlier successful call, a failure while adding a later entry discards all prior entries and leaves the same dangling pointer. Request teardown then double-frees the context.

Separately, when Line 1058 takes goto err because store is NULL, evrpc_hook_context_free_ computes &ctx->meta_data on a NULL pointer at Line 1042.

All three paths require an allocation failure, so normal operation is unaffected. This file is a vendored upstream import. Do not patch it locally. Report the defect to the libevent project and pick up the fix in the next vendor drop.

As per path instructions, "Ask for explicit user confirmation before editing bundled or separately maintained library code under lib/; keep such patches isolated and document their upstream project/path."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/evrpc.c` around lines 1076 - 1083, Do not
modify the vendored libevent code in this change. Report the dangling
req->hook_meta and NULL-handling defects to the libevent project, then schedule
the correction for the next vendor drop; if an update is prepared later, keep it
isolated and document the upstream project and path, requesting explicit
confirmation before editing code under lib/.

Source: Path instructions

Comment on lines +340 to +346
line = evbuffer_readln(input, &nread, EVBUFFER_EOL_CRLF);
if (!strncmp(line, "HTTP/1.1 401 ", strlen("HTTP/1.1 401 "))) {
test_ok++;
}
if (line)
free(line);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Null pointer dereference in http_ws_readcb_bad.

Line 340 assigns the result of evbuffer_readln, which returns NULL when the input buffer holds no complete CRLF-terminated line. Line 341 then calls strncmp(line, ...) before the NULL check at line 344.

The read callback fires whenever data arrives. A response split across TCP segments delivers a partial line first, so readln returns NULL and the test process crashes.

Move the NULL check before the comparison.

🐛 Proposed fix
 	line = evbuffer_readln(input, &nread, EVBUFFER_EOL_CRLF);
-	if (!strncmp(line, "HTTP/1.1 401 ", strlen("HTTP/1.1 401 "))) {
-		test_ok++;
-	}
-	if (line)
+	if (!line)
+		return;
+	if (!strncmp(line, "HTTP/1.1 401 ", strlen("HTTP/1.1 401 "))) {
+		test_ok++;
+	}
 		free(line);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
line = evbuffer_readln(input, &nread, EVBUFFER_EOL_CRLF);
if (!strncmp(line, "HTTP/1.1 401 ", strlen("HTTP/1.1 401 "))) {
test_ok++;
}
if (line)
free(line);
}
line = evbuffer_readln(input, &nread, EVBUFFER_EOL_CRLF);
if (!line)
return;
if (!strncmp(line, "HTTP/1.1 401 ", strlen("HTTP/1.1 401 "))) {
test_ok++;
}
free(line);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/test/regress_ws.c` around lines 340 - 346,
In http_ws_readcb_bad, check whether evbuffer_readln returned a non-NULL line
before passing it to strncmp; only perform the HTTP/1.1 401 comparison and
increment test_ok for a valid line, then free it safely afterward.

Comment on lines +98 to +102
} else if (events & BEV_EVENT_EOF) {
bufferevent_free(bev);
if (num_requests == MAX_REQUESTS) {
event_base_loopbreak(bufferevent_get_base(bev));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Fix the use-after-free of bev in server_event_cb.

Line 99 frees bev. Line 101 then calls bufferevent_get_base(bev) on the freed object. Read the base before the free.

🐛 Proposed fix
 	} else if (events & BEV_EVENT_EOF) {
+		struct event_base *base = bufferevent_get_base(bev);
 		bufferevent_free(bev);
 		if (num_requests == MAX_REQUESTS) {
-			event_base_loopbreak(bufferevent_get_base(bev));
+			event_base_loopbreak(base);
 		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (events & BEV_EVENT_EOF) {
bufferevent_free(bev);
if (num_requests == MAX_REQUESTS) {
event_base_loopbreak(bufferevent_get_base(bev));
}
} else if (events & BEV_EVENT_EOF) {
struct event_base *base = bufferevent_get_base(bev);
bufferevent_free(bev);
if (num_requests == MAX_REQUESTS) {
event_base_loopbreak(base);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/test/test-fdleak.c` around lines 98 - 102,
Update server_event_cb so it obtains and stores the event base from bev before
calling bufferevent_free(bev), then use the stored base in the num_requests ==
MAX_REQUESTS loopbreak path instead of accessing the freed bufferevent.

Comment on lines 185 to +186
num_requests++;
if (num_requests == MAX_REQUESTS) {
event_base_loopbreak(base);
} else {
if (++num_requests < MAX_REQUESTS) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the duplicate increment of num_requests.

Line 185 increments num_requests. Line 186 increments it again. Each completed request advances the counter by two. The client then starts about half the intended requests. The server check num_requests == MAX_REQUESTS at line 100 can also skip the exact value and never break the loop.

🐛 Proposed fix
 	num_requests++;
-	if (++num_requests < MAX_REQUESTS) {
+	if (num_requests < MAX_REQUESTS) {
 		start_client(base);
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
num_requests++;
if (num_requests == MAX_REQUESTS) {
event_base_loopbreak(base);
} else {
if (++num_requests < MAX_REQUESTS) {
num_requests++;
if (num_requests < MAX_REQUESTS) {
start_client(base);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_core/deps/libevent/test/test-fdleak.c` around lines 185 - 186,
Remove the standalone num_requests++ immediately before the condition, leaving
the increment in the if condition as the sole update per completed request.
Preserve the existing MAX_REQUESTS comparison and request-loop behavior so the
counter can reach the exact limit.

Comment on lines +517 to +525
if (mk_channel_is_empty(&conn->channel) == 0 &&
mk_net_transport_event_interest(conn->net,
event->fd,
MK_EVENT_READ) == MK_EVENT_WRITE) {
ret = mk_sched_event_read(conn, sched, server);
}
else {
ret = mk_sched_event_write(conn, sched, server);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

A combined READ and WRITE event can call the read handler twice.

An event mask can carry both MK_EVENT_WRITE and MK_EVENT_READ. When the new branch at Line 521 runs, Line 528 still matches and calls mk_sched_event_read a second time in the same iteration. If the first call returned -1, the protocol handler may have already removed the session, so the second call operates on freed memory. The previous code always ran the write handler in the write branch, so this path did not exist.

Guard the read branch at Line 528, or return the connection state before it runs again.

🐛 Proposed fix
                 if (event->mask & MK_EVENT_WRITE) {
                     MK_TRACE("[FD %i] Event WRITE", event->fd);
                     if (mk_channel_is_empty(&conn->channel) == 0 &&
                         mk_net_transport_event_interest(conn->net,
                                                         event->fd,
                                                         MK_EVENT_READ) == MK_EVENT_WRITE) {
                         ret = mk_sched_event_read(conn, sched, server);
+                        goto check_result;
                     }
                     else {
                         ret = mk_sched_event_write(conn, sched, server);
                     }
                 }
 
-                if (event->mask & MK_EVENT_READ) {
+                if (ret != -1 && (event->mask & MK_EVENT_READ)) {
                     MK_TRACE("[FD %i] Event READ", event->fd);
                     ret = mk_sched_event_read(conn, sched, server);
                 }

Use one of the two guards, not both.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/mk_server/mk_server.c` around lines 517 - 525, Prevent the
combined READ/WRITE event path from invoking mk_sched_event_read twice in one
iteration. Update the surrounding event-dispatch logic near the shown
mk_sched_event_read/mk_sched_event_write calls so that, after the first read
handling, the later read branch is skipped or the connection state is returned
before it can run again; use only one guard strategy.

Comment on lines +436 to +469
sent = 0;
remain = file_count;

do {
used = pread(file_fd, buf, SENDFILE_BUF_SIZE, *file_offset);
if (used == 0) {
ret = 0;
}
else if (used < 0) {
ret = -1;
}
else if (remain > 0) {
ret = SSL_write(ssl, buf, used < remain ? used : remain);
}
else {
ret = SSL_write(ssl, buf, used);
}

if (ret > 0) {
if (remain > 0) {
remain -= ret;
}
sent += ret;
*file_offset += ret;
}
} while (ret > 0);

mk_api->mem_free(buf);

if (sent > 0) {
return sent;
}

return tls_handle_return(ssl, ret);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

The send loop sends more bytes than file_count.

remain starts at file_count. When remain reaches 0, the loop does not stop. The next iteration takes the else branch at Line 450 and writes the full used bytes. The server then sends data past the requested length, which corrupts the response body for Content-Length and Range responses.

Also, sent is ssize_t but the function returns int. For a transfer above INT_MAX bytes, the return value overflows.

Stop the loop when remain reaches 0, and clamp the return value.

🐛 Proposed fix
     sent = 0;
     remain = file_count;
 
-    do {
+    while (remain > 0) {
         used = pread(file_fd, buf, SENDFILE_BUF_SIZE, *file_offset);
         if (used == 0) {
             ret = 0;
+            break;
         }
-        else if (used < 0) {
+        if (used < 0) {
             ret = -1;
+            break;
         }
-        else if (remain > 0) {
-            ret = SSL_write(ssl, buf, used < remain ? used : remain);
-        }
-        else {
-            ret = SSL_write(ssl, buf, used);
-        }
+        ret = SSL_write(ssl, buf, used < remain ? used : remain);
 
-        if (ret > 0) {
-            if (remain > 0) {
-                remain -= ret;
-            }
-            sent += ret;
-            *file_offset += ret;
+        if (ret <= 0) {
+            break;
         }
-    } while (ret > 0);
+        remain -= ret;
+        sent += ret;
+        *file_offset += ret;
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/plugins/tls/tls_openssl.c` around lines 436 - 469, Update the send
loop around remain and sent so it stops once the requested file_count has been
transmitted and never calls SSL_write with bytes beyond the remaining count.
Preserve partial-write handling and clamp the final sent return to the
function’s int range before returning, including the successful transfer path.

Comment on lines +603 to +612
static mbedtls_ssl_context *context_new(int fd)
{
struct polar_thread_context *thctx = local_thread_context();
struct polar_context_head **cur = &thctx->contexts;
mbedtls_ssl_context *ssl = NULL;
mbedtls_ssl_cache_context cache;

mbedtls_ssl_cache_init(&cache);

assert(cur != NULL);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Guard against a NULL thread context and remove the unused cache variable.

context_new dereferences thctx->contexts at line 606 without checking thctx for NULL. local_thread_context() returns NULL for any thread that did not run mk_tls_thread_init. The comment at line 560 states that contexts may be requested from outside workers, and context_get already handles that case. The call path mk_tls_readcontext_get (returns NULL when thctx is NULL) → context_new therefore crashes.

The local cache at line 608 is initialized and never used.

🛡️ Proposed fix
 static mbedtls_ssl_context *context_new(int fd)
 {
     struct polar_thread_context *thctx = local_thread_context();
-    struct polar_context_head **cur = &thctx->contexts;
+    struct polar_context_head **cur;
     mbedtls_ssl_context *ssl = NULL;
-    mbedtls_ssl_cache_context cache;
 
-    mbedtls_ssl_cache_init(&cache);
+    if (thctx == NULL) {
+        return NULL;
+    }
 
-    assert(cur != NULL);
+    cur = &thctx->contexts;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static mbedtls_ssl_context *context_new(int fd)
{
struct polar_thread_context *thctx = local_thread_context();
struct polar_context_head **cur = &thctx->contexts;
mbedtls_ssl_context *ssl = NULL;
mbedtls_ssl_cache_context cache;
mbedtls_ssl_cache_init(&cache);
assert(cur != NULL);
static mbedtls_ssl_context *context_new(int fd)
{
struct polar_thread_context *thctx = local_thread_context();
struct polar_context_head **cur;
mbedtls_ssl_context *ssl = NULL;
if (thctx == NULL) {
return NULL;
}
cur = &thctx->contexts;
assert(cur != NULL);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/tls/tls_mbedtls.c` around lines 603 - 612, Update context_new to
check the result of local_thread_context() before dereferencing it, returning
the existing failure value when no thread context is available; preserve the
context allocation flow for initialized threads. Remove the unused
mbedtls_ssl_cache_context cache declaration and its mbedtls_ssl_cache_init call.

Comment on lines +684 to +758
static int mk_tls_read(struct mk_plugin *plugin, int fd, void *buf, int count)
{
size_t avail;
mbedtls_ssl_context *ssl = context_get(fd);

(void) plugin;

if (!ssl) {
ssl = context_new(fd);
}

int ret = handle_return(ssl, mbedtls_ssl_read(ssl, buf, count));
MK_TRACE("IN: %i SSL READ: %i ; CORE COUNT: %i",
ssl->in_msglen,
ret, count);

/* Check if the caller read less than the available data */
if (ret > 0) {
avail = polar_get_bytes_avail(ssl);
if (avail > 0) {
/*
* A read callback would never read in buffer more than
* the size specified in 'count', but it aims to return
* as value the total information read in the buffer plugin
*/
ret += avail;
}
}
return ret;
}

static int mk_tls_write(struct mk_plugin *plugin, int fd, const void *buf, size_t count)
{
mbedtls_ssl_context *ssl = context_get(fd);
(void) plugin;
if (!ssl) {
ssl = context_new(fd);
}

return handle_return(ssl, mbedtls_ssl_write(ssl, buf, count));
}

static int mk_tls_writev(struct mk_plugin *plugin, int fd, struct mk_iov *mk_io)
{
mbedtls_ssl_context *ssl = context_get(fd);
const int iov_len = mk_io->iov_idx;
const struct mk_iovec *io = mk_io->io;
const size_t len = mk_io->total_len;
unsigned char *buf;
size_t used = 0;
int ret = 0, i;

(void) plugin;

if (!ssl) {
ssl = context_new(fd);
}

buf = mk_mem_alloc(len);
if (buf == NULL) {
mk_err("malloc failed: %s", strerror(errno));
return -1;
}

for (i = 0; i < iov_len; i++) {
memcpy(buf + used, io[i].iov_base, io[i].iov_len);
used += io[i].iov_len;
}

assert(used == len);
ret = mbedtls_ssl_write(ssl, buf, len);
mk_mem_free(buf);

return handle_return(ssl, ret);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Check the SSL context for NULL before use in the transport callbacks.

context_new returns NULL when mk_mem_alloc fails, and it returns NULL after the fix for the missing thread context. mk_tls_read, mk_tls_write, and mk_tls_writev pass ssl straight to handle_return and to the mbedTLS calls. handle_return dereferences ssl->p_bio at line 178, so a NULL context crashes the worker. mk_tls_send_file at line 763 has the same gap. The OpenSSL backend in lib/monkey/tls/tls_openssl.c already returns -1 on a NULL context; apply the same pattern here.

🛡️ Proposed fix for `mk_tls_read` (apply the same pattern to write, writev, and send_file)
     if (!ssl) {
         ssl = context_new(fd);
+        if (!ssl) {
+            return -1;
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/tls/tls_mbedtls.c` around lines 684 - 758, Add an immediate NULL
check after context creation in mk_tls_read, mk_tls_write, mk_tls_writev, and
mk_tls_send_file; return -1 when context_new or context_get yields no SSL
context, before any mbedTLS call or handle_return invocation. Match the existing
OpenSSL backend behavior and leave normal context handling unchanged.

Comment on lines +105 to +133
static const char tls_builtin_key[] =
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCpkF9Ks9aKiZao\n"
"yX+2CgOcAkX+6zkSonRvWfYtrS3m24zsc+co2xGx29nUW3sRSg6uNe1Jpi/8PKZw\n"
"ucZNXnQiKbab2pyLbWDfchakGeiYrO2EnIemdEYTB9MUUM1LtD/8LevLzKV8OBAa\n"
"vSHAdvv17OuIK5vgxEDmRF9nlXNrvvNoMxURsOSz0Rtoe/H1uhaLcNkBUjueM5ay\n"
"pKUFuiUbggTWHtEzBbPN584Wz1ewN/f+hXHOtoLc5cC+zhr2IVV0Z8J8v0T4a+51\n"
"DJ8FeZ+fG1H+N/Zwyv/EcjajhavMcnE8b+qKgNkWqaArRz6cagQSiA6I2roQVbKs\n"
"BUsXfWgpAgMBAAECggEADWuazyvKqC5ZmURRclP6kydu6M0vODVZZ9LD9DuHrYTk\n"
"83X87rPgA6a15+PRqr2kyc8E19ZqZ9lZBwT9F/SI1odcp5s21qYyi5zZA+X1Ddhp\n"
"+Bv3dIoxXaI555q5lOtQQSJVTk0FL/6z75nWiQghywYUYjOpY7HEvTTeJDGk7/sN\n"
"Bozc5Bczn5W6z7asKaXt7nC0WwauNMJ18WwKRjJlwOgBhb0/Qj5fqy1IxPI/kPwY\n"
"eAoIi91ARg/MCkUb4Yh6LTCfkwkElbpEIPr1T+2hYYl/x3wlvOUuEb+eIoZLNZ5x\n"
"K7kHPBcDEtGyl4yKPV4ltlJ0/ie0lp8pmNmXDNfV8QKBgQDUDx5dn833A1k1JMpm\n"
"XIjKOtIeaTRT58a2yM/T7de9oKRgC4hgEQ4cXG+R6r8n9/zLm3/m+l8+rH/tdBMl\n"
"C1Ekn8dL1be5zPpxDJlUSyQcYEJyDo2RHCW6Jgd6hrh73RmeFF5Tr4dFJcVVybZN\n"
"qAJPX3UcSrgmwrvqsXKkVXjrVQKBgQDMsw8eJzWDGErFLN43iE8UVD2oymgr8pq9\n"
"RcdoTX/MDHjwqUj0vGoY+IFE/sFXr100kqxH9ao5UNHn6yxed425m3wNP7O1rRqi\n"
"Nsu4WVsfsJ1J5n1Gbs6ujJ2TSiMG+a3fVr93T+6c+ysxMsWcz3gWZcbs4duRQ3Iw\n"
"NtM2KOCRhQKBgQDCTgAS5WSB222YBlf2pv8n3fG9r8QkxZEM1r+nfp1ZwaIb5zVU\n"
"YQw+7GvGlgQFiXL21UrCx9MRyFmHp/4KyW3WUxj34aHw+2LWxyaPWDKEVadMfw00\n"
"U0g2YrYjjOHpjNP2Rs+PepxFvbAtRSBn03QaamsSO1y1F2W8TE+xSCf96QKBgFlQ\n"
"a3E5pGSdzcn4iMDsLazuELU8E3XRdejNsHL3FaK/cml3Q4jdSOG6VBT5nvyWXHGa\n"
"6abALtSxSdUKTKKvQVxR1i+lstC7RdqvU/YMrvDFy+s5sUFxCacpXXutpljdyhqf\n"
"rAzwCGngQXlG8Og5sej74W7sITRhnEojMcb40PtNAoGAGawnWAi0AkIOVtRMBema\n"
"QZmW3tdVj798XHCI/8cl0CvsgctDdFmku759j4AUIlrAcn/R+umQwSnPAwwNXGV3\n"
"spDlSVoSDk7lYS4lVCWYUH+BCxqF+Ytb3IlJlv/FtKxCP4eiD1aCIbm9D/WCwBtf\n"
"36AgOGpW2UA1O65QO4j7HeU=\n"
"-----END PRIVATE KEY-----\n";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Remove the embedded private key and do not silently fall back to it.

Lines 105-133 store a complete RSA private key in source. The matching certificate is at lines 84-103. Any deployment that uses this key has no confidentiality or authenticity, because the key is public in the repository. Static analysis also flags this key.

tls_load_credentials at lines 435-443 makes this reachable in production: when SSL_CTX_use_certificate_chain_file fails for any reason, for example a missing or unreadable file, the function loads the built-in credentials and returns 0. The server then starts and serves HTTPS with a known key. A warning is printed, but startup is not blocked.

Fail startup when the configured certificate cannot be loaded. If a development fallback is required, gate it behind an explicit opt-in option and generate the key at runtime instead of committing it.

🔒️ Proposed change to the failure path
 static int tls_load_credentials(struct tls_server_context *ctx)
 {
     if (SSL_CTX_use_certificate_chain_file(ctx->ctx, ctx->config.cert_file) != 1) {
         mk_warn("[tls] failed to load certificate chain from %s",
                 ctx->config.cert_file);
-        mk_warn("[tls] using built-in development certificate, please configure CertificateFile/RSAKeyFile for production");
-        if (tls_load_builtin_credentials(ctx) != 0) {
-            return -1;
-        }
-        return 0;
+        mk_err("[tls] configure CertificateFile and RSAKeyFile before enabling TLS");
+        return -1;
     }
🧰 Tools
🪛 Betterleaks (1.7.0)

[high] 106-133: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/monkey/tls/tls_openssl.c` around lines 105 - 133, Remove the
tls_builtin_key and its matching embedded certificate, then update
tls_load_credentials to return failure when SSL_CTX_use_certificate_chain_file
or related configured-credential loading fails. Do not silently load built-in
credentials; if a development fallback is required, require an explicit opt-in
and generate credentials at runtime.

Source: Linters/SAST tools

@cosmo0920 cosmo0920 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without static linking settings of libevent, Windows tests are failed and introduced dynamic dependency of libevent:


Run dumpbin /dependents .\bin\fluent-bit.exe
  dumpbin /dependents .\bin\fluent-bit.exe
  shell: C:\Program Files\PowerShell\7\pwsh.EXE -command ". '{0}'"
  env:
    PATH: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\bin\HostX86\x86;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\VC\VCPackages;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TestWindow;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer;C:\Program Files\Microsoft Visual Studio\18\Enterprise\MSBuild\Current\bin\Roslyn;C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools\;C:\Program Files (x86)\HTML Help Workshop;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\CommonExtensions\Microsoft\FSharp\Tools;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Team Tools\DiagnosticsHub\Collector;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\Extensions\Microsoft\CodeCoverage.Console;C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\\x86;C:\Program Files (x86)\Windows Kits\10\bin\\x86;C:\Program Files\Microsoft Visual Studio\18\Enterprise\\MSBuild\Current\Bin\amd64;C:\Windows\Microsoft.NET\Framework\v4.0.30319;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\Tools\;C:\WinFlexBison;C:\ProgramData\Chocolatey\bin;c:/Program Files/Git/cmd;c:/Windows/system32;C:/Windows/System32/WindowsPowerShell/v1.0;$ENV:WIX/bin;C:/Program Files/CMake/bin;C:\vcpkg;;C:\Program Files (x86)\Microsoft Visual Studio\Installer;C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\Llvm\x64\bin;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja;C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\VC\Linux\bin\ConnectionManagerExe;C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\vcpkg
    CommandPromptType: Native
    DevEnvDir: C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\
    ExtensionSdkDir: C:\Program Files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs
    EXTERNAL_INCLUDE: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include;C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\ATLMFC\include;C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\VS\include;C:\Program Files (x86)\Windows Kits\10\include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um;C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\shared;C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\winrt;C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\cppwinrt;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8.1\include\um
    Framework40Version: v4.0
    FrameworkDir: C:\Windows\Microsoft.NET\Framework\
    FrameworkDir32: C:\Windows\Microsoft.NET\Framework\
    FrameworkVersion: v4.0.30319
    FrameworkVersion32: v4.0.30319
    FSHARPINSTALLDIR: C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\CommonExtensions\Microsoft\FSharp\Tools
    HTMLHelpDir: C:\Program Files (x86)\HTML Help Workshop
    INCLUDE: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include;C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\ATLMFC\include;C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\VS\include;C:\Program Files (x86)\Windows Kits\10\include\10.0.26100.0\ucrt;C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um;C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\shared;C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\winrt;C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\cppwinrt;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8.1\include\um
    LIB: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\ATLMFC\lib\x86;C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\lib\x86;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8.1\lib\um\x86;C:\Program Files (x86)\Windows Kits\10\lib\10.0.26100.0\ucrt\x86;C:\Program Files (x86)\Windows Kits\10\\lib\10.0.26100.0\\um\x86
    LIBPATH: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\ATLMFC\lib\x86;C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\lib\x86;C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\lib\x86\store\references;C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0;C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0;C:\Windows\Microsoft.NET\Framework\v4.0.30319
    llvmArm64: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\Llvm\ARM64\bin
    llvmX64: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\Llvm\x64\bin
    NETFXSDKDir: C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8.1\
    Platform: x86
    UCRTVersion: 10.0.26100.0
    UniversalCRTSdkDir: C:\Program Files (x86)\Windows Kits\10\
    use_x64_llvm: true
    VCIDEInstallDir: C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\IDE\VC\
    VCINSTALLDIR: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\
    VCPKG_ROOT: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\vcpkg
    VCToolsInstallDir: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\
    VCToolsRedistDir: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Redist\MSVC\14.51.36231\
    VCToolsVersion: 14.51.36231
    VisualStudioVersion: 18.0
    VS180COMNTOOLS: C:\Program Files\Microsoft Visual Studio\18\Enterprise\Common7\Tools\
    VSCMD_ARG_app_plat: Desktop
    VSCMD_ARG_HOST_ARCH: x86
    VSCMD_ARG_TGT_ARCH: x86
    VSCMD_VER: 18.8.2
    VSINSTALLDIR: C:\Program Files\Microsoft Visual Studio\18\Enterprise\
    VSSDK150INSTALL: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VSSDK
    VSSDKINSTALL: C:\Program Files\Microsoft Visual Studio\18\Enterprise\VSSDK
    WindowsLibPath: C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.26100.0;C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0
    WindowsSdkBinPath: C:\Program Files (x86)\Windows Kits\10\bin\
    WindowsSdkDir: C:\Program Files (x86)\Windows Kits\10\
    WindowsSDKLibVersion: 10.0.26100.0\
    WindowsSdkVerBinPath: C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\
    WindowsSDKVersion: 10.0.26100.0\
    WindowsSDK_ExecutablePath_x64: C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools\x64\
    WindowsSDK_ExecutablePath_x86: C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools\
    __DOTNET_ADD_32BIT: 1
    __DOTNET_PREFERRED_BITNESS: 32
    __VSCMD_PREINIT_PATH: C:\WinFlexBison;C:\ProgramData\Chocolatey\bin;c:/Program Files/Git/cmd;c:/Windows/system32;C:/Windows/System32/WindowsPowerShell/v1.0;$ENV:WIX/bin;C:/Program Files/CMake/bin;C:\vcpkg;;C:\Program Files (x86)\Microsoft Visual Studio\Installer
Microsoft (R) COFF/PE Dumper Version 14.51.36252.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file .\bin\fluent-bit.exe

File Type: EXECUTABLE IMAGE

  Image has the following dependencies:

    WS2_32.dll
    CRYPT32.dll
    bcrypt.dll
    SHLWAPI.dll
    tdh.dll
    ole32.dll
    wevtapi.dll
    NETAPI32.dll
    pdh.dll
    Secur32.dll
    event.dll
    ADVAPI32.dll
    IPHLPAPI.DLL
    KERNEL32.dll
    USER32.dll
    OLEAUT32.dll
LINK : warning LNK4078: multiple '.text' sections found with different attributes (C0000040)

  Summary

       36000 .data
        1000 .fptable
      32D000 .rdata
       4B000 .reloc
        1000 .rsrc
        1000 .text
      82A000 .text

This could be ideal for us.

So, we need to add a setting for setting up static linking of libevent like as:

# Monkey uses its bundled libevent backend on Windows. Link it statically so
# Fluent Bit binaries do not require event.dll at runtime.
if(FLB_SYSTEM_WINDOWS)
  set(EVENT__LIBRARY_TYPE STATIC CACHE STRING
      "Build Monkey's bundled libevent as a static library" FORCE)
endif()

kimonus added a commit to kimonus/fluent-bit that referenced this pull request Aug 4, 2026
Monkey's parser fix was merged upstream in monkey/monkey#444 and is
being bundled by fluent#12212. This PR was therefore
rebased onto fluent#12212's lib-monkey-1.8.9 branch, removing its direct
modifications to the vendored Monkey sources.

This commit keeps only Fluent Bit's parser-error propagation. The PR
temporarily depends on fluent#12212 and must be rebased onto master again
after fluent#12212 merges and before this PR is merged.

Signed-off-by: kimonus <kimonus@users.noreply.github.com>
kimonus added a commit to kimonus/fluent-bit that referenced this pull request Aug 4, 2026
Send real HTTP/1 requests containing empty and whitespace-only generic
headers and require the accepted request to reach the input callback.
Also verify that an invalid empty Upgrade field is not ingested.

Use portable socket types, handle partial writes and reads, and reject
timeouts or incomplete HTTP status lines so the regression runs on the
supported runtime-test platforms.

This test is prepared against Monkey 1.8.9 from fluent#12212. The PR remains
stacked on that bundle update until fluent#12212 merges, then it will be
rebased onto master before merge.

Signed-off-by: kimonus <kimonus@users.noreply.github.com>
kimonus added a commit to kimonus/fluent-bit that referenced this pull request Aug 4, 2026
Add real-server POST coverage matching fluent#12174 for empty and
whitespace-only generic fields, and verify that the request body is
forwarded successfully.

Cover empty Connection and Transfer-Encoding values plus empty and
whitespace-only Content-Length fields followed immediately by numeric
header or body data. Rejected requests must close or return 400, never
hang or forward a payload.

These tests were prepared and validated against Monkey 1.8.9 from
fluent#12212. The PR will be rebased from that temporary branch onto master
after fluent#12212 merges.

Signed-off-by: kimonus <kimonus@users.noreply.github.com>
Signed-off-by: Eduardo Silva <eduardo@chronosphere.io>
@edsiper
edsiper merged commit f60530a into master Aug 4, 2026
53 of 57 checks passed
@edsiper
edsiper deleted the lib-monkey-1.8.9 branch August 4, 2026 15:51
edsiper pushed a commit that referenced this pull request Aug 5, 2026
The Monkey parser fix was merged upstream in monkey/monkey#444 and is
bundled in Fluent Bit by #12212 as part of Monkey 1.8.9.

Propagate MK_HTTP_PARSER_ERROR to the HTTP server provider so malformed
requests are closed instead of resetting the parser and remaining pending.

Keep this Fluent Bit-specific error handling separate from the bundled
Monkey sources.

Signed-off-by: kimonus <kimonus@users.noreply.github.com>
edsiper pushed a commit that referenced this pull request Aug 5, 2026
Send real HTTP/1 requests containing empty and whitespace-only generic
headers and require the accepted request to reach the input callback. Also
verify that an invalid empty Upgrade field is not ingested.

Use portable socket types, handle partial writes and reads, and reject
timeouts or incomplete HTTP status lines so the regression runs on the
supported runtime-test platforms.

The test exercises Monkey 1.8.9 now bundled in Fluent Bit by #12212.

Signed-off-by: kimonus <kimonus@users.noreply.github.com>
edsiper pushed a commit that referenced this pull request Aug 5, 2026
Add real-server POST coverage matching #12174 for empty and
whitespace-only generic fields, and verify that the request body is
forwarded successfully.

Cover empty Connection and Transfer-Encoding values plus empty and
whitespace-only Content-Length fields followed immediately by numeric
header or body data. Rejected requests must close or return 400, never
hang or forward a payload.

These tests exercise Monkey 1.8.9 now bundled in Fluent Bit by #12212.

Signed-off-by: kimonus <kimonus@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants