OCaml Weekly News

Previous Week Up Next Week

Hello

Here is the latest OCaml Weekly News, for the week of August 18 to 25, 2026.

Table of Contents

OSEC-2026-16: cohttp: path traversal

Hannes Mehnert announced

Dear everyone,

we just published a security advisory for cohttp - please find it below (and as usual at https://github.com/ocaml/security-advisories and https://osv.dev/list?q=&ecosystem=opam)

Best,

Hannes

id: OSEC-2026-16
modified: "2026-08-20T18:15:00Z"
published: "2026-08-20T18:15:00Z"
severity: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"
severity_score: "7.5 (High)"
affected: "cohttp" {< "6.3.0"}
events: [
   [
     git "https://github.com/mirage/ocaml-cohttp.git" [
       [fixed "5f5a65ec3289c1cd8072bdf0ef22c181b4f11356"]
     ]
   ]
]
credits: [
   [reporter "Sapphire Livingstone"]
   [remediation_developer "Sapphire Livingstone"]
   [remediation_developer "Anil Madhavapeddy"]
   [remediation_reviewer "Anil Madhavapeddy"]
   [remediation_reviewer "Michael Dales"]
   [remediation_reviewer "Edwin Torok"]
   [remediation_reviewer "Patrick Ferris"]
   [coordinator "Hannes Mehnert"]
]
cwe: [ CWE-22 ]
affected_bindings: [
   "Cohttp.Path.resolve_local_file"
   "Cohttp_async.Server.resolve_local_file"
   "Cohttp_lwt_unix.Server.resolve_file"
   "Cohttp_lwt.Make().resolve_local_file"
]

Path traversal in Cohttp.Path.resolve_local_file

The issue is that the function normalizes the URI path before percent decoding it:

let resolve_local_file ~docroot ~uri =

   let path = Uri.(pct_decode (path (resolve "http" (of_string "/") 
uri))) in

   ...

Because %2f is decoded after Uri.resolve, encoded separators survive dot-segment normalization. For example, a request path like:

/static/..%2f..%2f..%2fetc/passwd

is normalized as a single encoded segment, then decoded into:

/static/../../../etc/passwd

afterwards.

Timeline

Cascade: A Typed CSS Toolkit in OCaml

Continuing this thread, Thomas Gazagnaire announced

I have just released cascade 1.1.0 (which was just merged in opam) which improves the diff output quite a bit (especially on CSS sheets that user layers, containers, medias or other fancy structural elements) and adds something I wanted to rely on for a long time but for some reasons didn't think about adding earlier: a differential testing harness to test how a (headless) browser interpret a random CSS sheet on optimized vs. non-optimized mode. This allowed me to find quite a few issues (most of them as corner cases, but some were a bit embarassing) and explain the size of the CHANGELOG.

Happy to get any feedback on it, especially if the documentation is useful at all (I have tried without success to get some magic odoc runes to embed runnable CSS fragments in there, is this possible at all?)

YAMLx 0.5.0 + odds things about the project

Martin Jambon announced

I just released YAMLx 0.5.0 (release notes) which provides a few fixes and improvements since the original announcement in April. YAMLx is a pure-OCaml library implementing the complex YAML 1.1 and 1.2 specifications. It aims to provide a complete yet convenient user interface to manipulate data in this popular format.

Beyond the boring announcement, this is an opportunity for me to bring your attention (again?) to the unusual aspects of the YAMLx project. It is sacrilegious and experimental along 3 main axes:

AI use

  • Most of the implementation is AI-generated (~10K lines).
  • The interfaces are mostly human-crafted (~800 lines).

Authorship model

  • The project has only one single traditional contributor.
  • Contributions are encouraged in the form of bug reports and detailed feature requests.
  • The project doesn't take external code contributions.

Funding

  • The library is available for free under a strong copyleft license (AGPL).
  • A one-time contribution ($500) gets your organization a perpetual commercial license.
  • Past the funding threshold ($5,000), everyone will get a permissive license for free (ISC).

— I'm happy to go over the motivations for these choices. Just ask.

Opam repository package contributors should have a human behind them

Anil Madhavapeddy announced

Dear all,

I wanted to draw your attention to a new bit of opam-repository contribution guidance that we have just included.

14. Package contributors accounts should have a human behind them The review work by the maintainers of opam-repository is carried out by humans, and we need to have a contact to respond to our feedback.

Reasoning:

  • A proposal for a new package can be submitted from bot accounts, but the PR should be tagged with a human contact so the opam-repo maintainers know who to talk to.
  • Subsequent discussions regarding the PR should avoid excessive noise, such as large LLM-generated responses or automated triage logs, to avoid overloading the maintainers.
  • Any machine generated comments on a package publication PR must be explicitly identified as such.
  • When human reviewers ask questions or provide guidance, they must be met with human replies.

The motivation here is twofold: we have a small but growing number of submissions from projects that have bot-based release procedures. In several cases, this means we've had to dig out who the responsible person actually is.

And of course, there's more and more LLM spam out there waiting to spring on our poor human faces, but the submissions PRs have so far been largely considerate. Let's keep that going! Feedback welcome as always and the PR discussion here for those interested, and keep those opam package submissions coming :-)

Cohttp 6.3.0 released (OSEC-2026-16)

Anil Madhavapeddy announced

Dear all,

cohttp 6.3.0 is released onto opam-repository with some important security fixes (OSEC-2026-16):

  • cohttp: Cohttp.Path.resolve_local_file no longer escapes the docroot when given percent-encoded traversal sequences such as ..%2f..%2f. The URI path is percent-decoded exactly once before . and .. segments are removed. (#1145 @avsm and Sapphire Livingstone, review by @mdales @edwintorok @patricoferris)
  • cohttp: Cohttp.Path.resolve_local_file collapses empty path segments and drops a trailing slash. A request for /dir//sub/ now resolves to docroot/dir/sub instead of docroot/dir//sub/. (#1145 @avsm)
  • cohttp: Add Cohttp.Path.normalise, which converts a request URI into a relative path that cannot ascend above its root. Servers that make access control decisions on path segments must apply it to Request.uri before inspecting them, as Request.uri does not normalise absolute-form or percent-encoded targets. (#1145 @avsm)

    let callback _conn req _body =
      let uri = Cohttp.Request.uri req in
      match String.split_on_char '/' (Cohttp.Path.normalise uri) with
      | "admin" :: _ when not (authorised req) -> Server.respond_not_found ()
      | _ ->
          let fname = Cohttp.Path.resolve_local_file ~docroot ~uri in
          Server.respond_file ~fname ()
    

    Normalisation is not applied by default, as existing code may depend on the present semantics, which are safe when not combined with local file resolution.

  • cohttp-mirage: The static file server normalises the request path of every request, including directory requests, before looking it up in the mirage-kv store. Keys are now percent-decoded, so /my%20file.txt retrieves the key my file.txt rather than my%20file.txt (#1145 @avsm, review by @mdales @edwintorok)
  • cohttp-mirage: The request_fn callback receives the request URI unchanged. A request that falls back to an index page previously received a URI rewritten to that page. (#1145 @avsm)
  • cohttp: do not add Transfer-Encoding~/~Content-Length framing headers to responses that cannot have a body (1xx, 204 and 304). This fixes WebSocket handshakes. (@mefyl @avsm, #1141)
  • http: add Status.body_allowed, the response-status counterpart to the existing Method.body_allowed, for users constructing responses by hand (#1141)

Please upgrade as soon as possible, and note the new normalise function for your own HTTP path splitting needs.

Thank you to everyone who helped with handling the security issue, and especially Sapphire Livingstone for the discovery, report and guidance with the fix:

  • Sapphire Livingstone - REPORTER
  • Sapphire Livingstone - REMEDIATION_DEVELOPER
  • Anil Madhavapeddy - REMEDIATION_DEVELOPER
  • Anil Madhavapeddy - REMEDIATION_REVIEWER
  • Michael Dales - REMEDIATION_REVIEWER
  • Edwin Torok - REMEDIATION_REVIEWER
  • Patrick Ferris - REMEDIATION_REVIEWER
  • Hannes Mehnert - COORDINATOR

opam 2.6.0~alpha1

Continuing this thread, Kate announced

Hi everyone,

We are happy to announce the release of opam 2.6.0~beta1.

This version is a beta, we invite users to test it to spot previously unnoticed bugs as we head towards the stable release.

Main changes compared to 2.6.0~alpha1

  • :recycling_symbol: opam init --reinit (command typically called after upgrading from an older opam version) got improved. It now stops asking to retry the command when upgrading from a 2.1 opam root, and regenerates switches metadata to allow tools that use the opam library to load the switch informations without modifying the opam root (#7057, #7066)
  • :houses: When --safe is given, opam used to reset debug-level to 0. This is no longer the case. Consider updating your scripts accordingly. (#7000)
  • :shuffle_tracks_button: opam now disable git gc/maintenance on repositories it maintains. This is because git spawns maintenance tasks in the background which creates/removes/modifies files and unaware opam processes can sometimes break on a race-condition when handling such a git repository (#7031)

:open_book: You can read our blog post for more information about these changes and more, and for even more details you can take a look at the release note or the changelog.

Try it!

The upgrade instructions are unchanged:

For Unix systems

bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh) --version 2.6.0~beta1"

or from PowerShell for Windows systems

Invoke-Expression "& { $(Invoke-RestMethod https://opam.ocaml.org/install.ps1) } -Version 2.6.0~beta1"

Please report any issues to the bug-tracker.

Happy hacking, <> <> The opam team <> <> :camel:

Jane Street Libraries (base/core/async/etc) with 5.5

Stefan Muenzel announced

(Note: much of the linked repos are modified with the the help of LLMs, so please do check the diff to the upstream version before installing. Most diffs are very small. Also, this release is not affiliated with Jane Street)

Since the current version of the Jane Street public release libraries are targeting either older version of the compiler, or OxCaml, I've made a version of the jane street opam repository that targets 5.5.

Almost all ~300 libraries are available (or at least build), with the exception of some heavy oxcaml users, such as skyline. Versions available are v0.18_preview.130.100+614 and v0.18_preview.130.106+341.

The opam repository is available at https://github.com/public-release-reloaded/opam-repository, and the repo used to generate this is at https://github.com/public-release-reloaded-engineering/public-release-reloaded

Note that you need ppxlib > 0.38.0, due to https://github.com/ocaml-ppx/ppxlib/pull/644

I believe things should mostly work, but please do comment about any issues that you have.

OCaml running on a 1960s UNIVAC 1219B

Nathan Farlow announced

Hi!

I've been meaning to post a recent-ish project of mine where I ran OCaml (and much more) on a 1960s UNIVAC 1219B! The demo I chose to run was a sudoku solver. In short, this was done by compiling OCaml to C, C to RISC-V, and then emulating RISC-V on the UNIVAC.

Full UNIVAC toolchain: Writeup, and source. Please see the my comment below for the other links! I'm unable to post more than two links in a post.

I also ran an OCaml program submitted by a friend that computed factors of numbers. At one point we thought the program crashed, but we were delighted when the program resumed ~5 minutes later. Turns out garbage collecting is slow on this machine :slight_smile:

Oh, and it works with OxCaml too!

Enjoy! Below are some pictures. (Disregard the earlier output, that was a run of ELIZA :slight_smile:)

f77dc661631e09e5aab6f5024997dc3556553524_2_750x1000.jpeg

571ce6aa512595b1f5cd130cd2b181ddcb3b6401_2_1332x1000.jpeg

Editor note: I’m inlining the links that were in the subsequent messages.

OCaml to C: Writeup, and source

Sudoku solver source: OCaml, and compiled to C

Old CWN

If you happen to miss a CWN, you can send me a message and I'll mail it to you, or go take a look at the archive or the RSS feed of the archives.

If you also wish to receive it every week by mail, you may subscribe to the caml-list.