louis-rs Security Audit

TL;DR

Shielder, together with OSTIF, performed a Security Audit of the louis-rs component, a pure Rust re-implementation of the liblouis braille translation and back-translation library.

The audit resulted in three (3) findings of medium severity. All of them have been addressed by the louis-rs maintainers.

Today, we are publishing the full report in our dedicated repository.

Introduction

In March 2026, Shielder was hired to perform a Security Audit of the louis-rs component, a pure Rust re-implementation still in an alpha state of liblouis. The audit was facilitated by the Open Source Technology Improvement Fund (OSTIF).

louis-rs is an open-source Braille translation library, its main functionality is converting text to Braille and Braille back to text.

Braille is a tactile writing system that represents letters, numbers, punctuation, and other symbols using patterns of raised dots that can be read by touch.

Braille translation tables, such as those used by liblouis and louis-rs, define the rules for converting between ordinary text and Braille, including language-specific conventions, contractions, and formatting.

The decision to rewrite liblouis in Rust was motivated by the fact that the vast majority of CVEs in liblouis, which is employed by all the major operating systems for translating Braille, are due to manual memory management. Therefore, Its maintainers opted for a memory-safe language that also provides a comfortable environment for development and maintenance.

louis-rs is built around the following core components:

  • CLI: the louis binary, which provides commands to parse braille tables, translate text to/from braille, trace rule application, run YAML-based test suites, and query table metadata.
  • Parser: the module that reads and expands liblouis braille table files.
  • Translator: the translation pipeline that compiles parsed rules into lookup structures and executes them in stages to convert text to braille (forward-translation) or braille to text (back-translation).
  • Braille tables: liblouis-format .ctb/.utb files that define the character mappings and contraction rules for a given language.

The source code is available at https://github.com/liblouis/louis-rs.

Context and Scope

The audit mainly focused on:

  • Assessing the risks of passing untrusted tables to the Parser.
  • Assessing the risks of passing untrusted text to translate and backtranslate to the Translator.
  • Assessing the resilience of regular expressions employed in the translation table from liblouis against ReDOS attacks.

Given the limited size of the codebase, the audit was mostly performed following a Manual Source Code Review approach coupled with SAST and LLM based analysis for a complete and thorough assessment of the code. We directed the review towards three classes of threats:

  • Logical flaws in the translation and backtranslation operations.
  • Errors while parsing the translation table.
  • Unhandled exceptions leading to denial of service.

One of the goals of the audit was to develop a set of fuzzers to dynamically stress some of the most complex parts of the library: table parsing and text translation.

For this purpose, we developed a custom fuzzer based on a modified version of rust-fuzz, configured to use the libafl_libfuzzer runtime instead of the default libfuzzer runtime. This configuration enables the fuzzer to leverage the built-in grimoire mutator, providing transparent and automated structure-aware fuzzing of the translation table syntax.

Findings Summary and Recommendations

The louis-rs component is, overall, adequately robust and well designed from a security standpoint - but there is still some room for improvement.

The Shielder team identified three (3) medium findings.

The main theme is the possibility to cause denial of services in the library via malicious input.

IDVulnerabilitySeverityStatus
1Denial of Service via Uncontrolled Recursion in Table InclusionMediumClosed
2Denial of Service via Regex Catastrophic Backtracking on Custom TablesMediumClosed
3Denial of Service via Panic on Double Negation in Match Rule PatternsMediumClosed

Details

All three findings fall within the family of denial of service vulnerabilities.

The first two findings both leverage mechanisms that cause the component to perform a huge amount of work in response to a small, malicious input. The third, instead, relies on a panic triggered by an unhandled edge case reachable again through a crafted input.

Denial of Service via Uncontrolled Recursion in Table Inclusion It is possible to cause a denial of service in louis-rs by causing an uncontrolled recursion via the abuse of the “include” directory, used by a translation table to reference and include an external table.

The library library does not verify if cycles arise during the inclusion process and does not implement a mechanism to stop the execution after a certain threshold of depth.

Therefore to cause a stack overflow it is sufficient to provide as input to the Parser a self referencing table.

It is possible to generate a malicious table to demonstrate the vulnerability simply as:

1
echo 'include evil.utb' > evil.utb

To trigger the overflow:

1
louis parse evil.utb

Denial of Service via Regex Catastrophic Backtracking on Custom Tables It is possible to cause a denial of service in louis-rs providing in input to the library a table containing malicious regular expressions capable of causing a catastrophic backtracking that exhausts memory.

The Translator does not verify how much backtracking it performs, being therefore susceptible to denial of service attacks based on regular expressions.

It is possible to generate a malicious table to demonstrate the vulnerability simply as:

1
echo 'match (a+)+ b b 1' > redos.utb

To trigger the uncontrolled backtracking:

1
louis translate redos.utb aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Denial of Service via Panic on Double Negation in Match Rule Patterns It is possible to cause a denial of service in louis-rs providing in input to the library a table containing malicious regular expressions that make the program panic.

When a double negation produces a NotString or NotCharacterClass variant and .negate() is called on it a second time, the program panics.

It is possible to generate a malicious table to demonstrate the vulnerability simply as:

1
echo 'match !!a b c 1' > crash.utb

To trigger the panic:

1
louis translate crash.utb "any text"

All three were reported to louis-rs maintainers and fixed by following the attached recommendations.

General Improvements

Beyond the findings, we supported the projects in the following areas:

  • Setup Dependency Scanning: We integrate proper jobs in the CI/CD pipeline to automate the process of SBOM generation, dependency scanning and license checking.
  • Setup CodeQL Scanning: We integrate CodeQL scanning in the CI/CD pipeline of the project.
  • Setup fuzzing: We gave the custom fuzzer to the project, so it can be easily integrated in OSS-Fuzz in the future.

Conclusions

The louis-rs component is still in an alpha state, yet it is already adequately secured.

The main threats arise from adversarial input that can cause unexpected behavior in the program.

All three findings exploit a malicious table as an entry point, which is a realistic scenario only for certain use cases of the library.

It is, however, worth noting that no attacks using the text to be translated as an input vector were identified during the assessment. This represents the main entry point for untrusted input to the library.

If you use louis-rs the recommendation is the following: update to the latest release and treat translation tables as a privileged artifact or as a source of untrusted input, depending on the specific deployment, putting in place adequate precautions.

We would like to thank the louis-rs maintainers - notably Christian Egli - for being responsive and collaborative throughout the triage and remediation of these findings.

It was a pleasure for our team to work with OSTIF and the louis-rs core team to help improve the security of their Rust library and push it toward a safer and more stable state.

Pitch 🗣

Did you know OSTIF helps sensitive open-source projects in securing funds to perform security audits? They will also help you in scoping the assessment, finding a trusted partner to perform the analysis, and ensuring full transparency along the way.

P.S. Developing a software heavily relying on untrusted input parsing? Get in touch with us! We will support your team into auditing the code and setting up efficient fuzzers for continuous adversarial testing!

4 min

Date

29 September 2026

Author

ndaprela

I’m nico. Security Researcher and Penetration Tester at Shielder.
I like breaking things.

Author

suidpit

Security Researcher and Penetration Tester at Shielder. Human, Chaotic Good. Disciple of Bushido & Disney.