Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
32b35cd
Initial fix for #487
TheElementalOfDestruction Sep 28, 2026
183ee39
Update readme
TheElementalOfDestruction Sep 28, 2026
c64bd73
Readme updates
TheElementalOfDestruction Sep 28, 2026
03cd7a0
Minor adjustment of credits order
TheElementalOfDestruction Sep 29, 2026
c384b65
Add new testing requirements
TheElementalOfDestruction Sep 29, 2026
cd13a69
Update text requirements
TheElementalOfDestruction Sep 29, 2026
0a4a7a3
Add test requirements to workflow
TheElementalOfDestruction Sep 29, 2026
557f4eb
Another minor credits order adjustment
TheElementalOfDestruction Sep 29, 2026
492a952
Add wk security test
TheElementalOfDestruction Sep 29, 2026
742df12
Fix for GHSA-mqxr-j8rh-p4q4
TheElementalOfDestruction Oct 7, 2026
3037573
Add changelog fragment
TheElementalOfDestruction Oct 7, 2026
58e1181
Fix bad check and do early stop for multiple
TheElementalOfDestruction Oct 7, 2026
6f1bb90
Fixes and adjustments to MSGFile.listDir()
TheElementalOfDestruction Oct 7, 2026
8c40d5b
Change `MSGFile.exists()` to use a cache instead of checking the enti…
TheElementalOfDestruction Oct 7, 2026
5cde8d3
Fix new exists being case sensitive
TheElementalOfDestruction Oct 7, 2026
98dd651
Add changelog fragment
TheElementalOfDestruction Oct 7, 2026
1565d9d
Fix `MSGFile._getTypedStream()`
TheElementalOfDestruction Oct 7, 2026
cf8ecd5
Correctly fix MSGFile._getTypedStream
TheElementalOfDestruction Oct 7, 2026
9f72edd
Update fragment
TheElementalOfDestruction Oct 7, 2026
c57b7b5
Changed handling of bad rtf data
TheElementalOfDestruction Oct 7, 2026
d45a1da
Fix early return none missing
TheElementalOfDestruction Oct 7, 2026
16eaa82
Update util_tests.py
TheElementalOfDestruction Oct 9, 2026
e28cedb
Update util_tests.py
TheElementalOfDestruction Oct 9, 2026
8ec64fb
Add test file
TheElementalOfDestruction Oct 9, 2026
166909d
fix typo
TheElementalOfDestruction Oct 12, 2026
25ae7cf
Update standard violation behavior of MSGBase
TheElementalOfDestruction Oct 23, 2026
596e44b
Add tests for multiple stream types
TheElementalOfDestruction Oct 23, 2026
9e51d42
Clean test files
TheElementalOfDestruction Oct 23, 2026
f851e47
Fix typing information for msg path
TheElementalOfDestruction Oct 23, 2026
a4b37c9
Fix ole writer not expecing path tuples
TheElementalOfDestruction Oct 23, 2026
f2d1e82
Add tests for multiple type streams
TheElementalOfDestruction Oct 23, 2026
caf3c61
Fix errors in example file and add additional tests
TheElementalOfDestruction Oct 24, 2026
5f25155
Fix example file (again)
TheElementalOfDestruction Oct 24, 2026
0fc63be
Add additional checks to multiple streams and add tests for checks
TheElementalOfDestruction Oct 24, 2026
8a13f7d
Update export-multi-test.py
TheElementalOfDestruction Oct 24, 2026
aff9bb3
Change issue reference, fix capitalization
TheElementalOfDestruction Oct 24, 2026
7d384b9
Update example file
TheElementalOfDestruction Oct 24, 2026
253faaa
Merge commit from fork
TheElementalOfDestruction Oct 24, 2026
951070a
Merge commit from fork
TheElementalOfDestruction Oct 24, 2026
5116aaf
Merge commit from fork
TheElementalOfDestruction Oct 24, 2026
58deb0c
Merge changelog fragments
TheElementalOfDestruction Oct 24, 2026
f27992c
Update security policy
TheElementalOfDestruction Oct 24, 2026
360d26b
Ensure all tests are active
TheElementalOfDestruction Oct 24, 2026
b053db3
Bump date and copyright
TheElementalOfDestruction Oct 24, 2026
7baac9b
Fix some issues with encodings
TheElementalOfDestruction Oct 24, 2026
92983fc
Update workflow to try and get wkhtmltopdf
TheElementalOfDestruction Oct 24, 2026
3b02409
Correctly try to use packages (forgot to specify that's what I was doing
TheElementalOfDestruction Oct 24, 2026
a0913aa
Remove action usage for pure apt, fix typo
TheElementalOfDestruction Oct 24, 2026
eba2d1f
Remove accidentally added line
TheElementalOfDestruction Oct 24, 2026
6f31da7
Add special case for python version 3.8 and 3.9
TheElementalOfDestruction Oct 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ Please include a description of the vulnerability, steps to reproduce,
the affected version, and impact assessment if possible.

I will acknowledge receipt within 72 hours and aim to release a fix
within 90 days. Public disclosure will be coordinated with the reporter.
within 90 days. Public disclosure will be coordinated with the reporter. If
the vulnerability cannot be confirmed and the opener does not respond within
90 days, your advisory may be automatically closed.
3 changes: 3 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f test_requirements.txt ]; then pip install -r test_requirements.txt; fi
- name: Install wkhtmltopdf
run: sudo apt install -y wkhtmltopdf
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ __pycache__/
!/example-msg-files/expected-outputs/2016-02-23_0657 MSG Test File/*
!/example-msg-files/strangeDate.msg
!/example-msg-files/unicode.msg
!/example-msg-files/multi-stream.msg
!/example-msg-files/multi-to.msg
!/example-msg-files/multi-to-to.msg
!/example-msg-files/unicode-header.msg
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
**v0.57.0**
* [[Advisory GHSA-cxpg-prmv-g4pp](https://github.com/TeamMsgExtractor/msg-extractor/security/advisories/GHSA-cxpg-prmv-g4pp)] Fixed a vulnerability related to unchecked input passed out to wkhtmltopdf when converting the MSG file to PDF. Javascript is now strongly blocked from being enabled and will be stripped from the HTML where appropriate before reaching wkhtmltopdf. Additionally, the option `--wk-allow-external-files` on the command line (or `wkAllowExternalFiles` to `MessageBase.save()`) will be required for external files to be loaded into the HTML to convert. Without these, they will be stripped before reaching wkhtmltopdf for security.
* [[Advisory GHSA-mqxr-j8rh-p4q4](https://github.com/TeamMsgExtractor/msg-extractor/security/advisories/GHSA-mqxr-j8rh-p4q4)] Fixed an issue where an attacker controlled MSG file could take advantage of a poorly written section of code in the RTF deencapsulation function on the `extract-msg` side to waste massive amounts of time and memory resources.
* [[Advisory GHSA-rgjf-vmqv-8pv9](https://github.com/TeamMsgExtractor/msg-extractor/security/advisories/GHSA-rgjf-vmqv-8pv9)] Change the way multiple type properties are accessed for binary and string. This includes modifications to `MSGFile.getMultipleBinary()`, `MSGFile.getMultipleString()`, and `MSGFile.exists()` to significantly reduce wasted times for malicious files. Malicious files previously could cause extract-msg to do unnecessarily large numbers of calls to `MSGFile.exists()` which previously did not use caching to speed up its search.
* Changed `MSGFile.listDir()` to return a tuple of tuples to prevent it's cache results from being tampered with.
* Fixed an issue with `MSGFile.listDir()` that caused it to not cache in certain circumstances when it should always cache.
* Fixed a check in `MSGFile.getMultipleBinary()` that executed wrong and did not properly check for bad data.
* Fixed `MSGFile._getTypedSteam()` to correctly parse binary and string streams when an entry is missing.
* Fixed `StandardViolationError` being raised without checking error behavior in `MSGFile.getMultipleBinary()` and `MSGFile.getMultipleString()`.
* Added check for standard violation in `MSGFile.getMultipleString()` that is raised (or logged, depending on error behavior) if a string stream is missing a null byte. This also doesn't strip the last character in the stream if it is not null.
* Added length check log to `MSGFile.getMultipleBinary()` and `MSGFile.getMultipleString()` that checks to see if the expected length for each stream matches what the length stream says. If not, it logs a warning about the mismatch, but does not consider this a gross violation of the standard.
* Changed how `MessageBase.deencapsulatedRtf` handles RTF data without a closing brace.
* Fixed wrong return being used in `extract_msg.encoding.utils.singleByteEncode()` and `extract_msg.encoding.utils.variableByteEncode()`.
* Fixed missing return in `extract_msg.encoding.utils.singleByteEncode()`.
* Fixed wrong parameter names for codec types made in `extract_msg.encoding.utils.createSBEncoding()` and `extract_msg.encoding.utils.createVBEncoding()`.
* Fix some values being incorrectly returned from encode/decode functions in `extract_msg.encoding.utils`.

**v0.56.1**
* [[TeamMsgExtractor #484](https://github.com/TeamMsgExtractor/msg-extractor/issues/484)] Corrected the way the HTML body is decoded in `MessageBase.asEmailMessage()` to prevent encoding errors.

Expand Down
112 changes: 62 additions & 50 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
|License: GPL v3| |PyPI3| |PyPI2| |Read the Docs|
|License: GPL v3| |PyPI3| |PyPI2| |Downloads| |Read the Docs|

extract-msg
=============
Expand All @@ -14,7 +14,7 @@ Documentation can be found in the code, on the `wiki`_, and on the
NOTICE
======
0.29.* is the branch that supports both Python 2 and Python 3. It is now only
receiving bug fixes and will not be receiving feature updates.
receiving security fixes and will not be receiving feature updates.

0.39.* is the last versions that supported Python 3.6 and 3.7. Support for those
was dropped to allow the use of new features from 3.8 and because the life spans
Expand Down Expand Up @@ -59,55 +59,59 @@ Currently, the README is in the process of being redone. For now, please
refer to the usage information provided from the program's help dialog:
::

usage: extract_msg [-h] [--use-content-id] [--json] [--file-logging] [-v] [--log LOG] [--config CONFIGPATH] [--out OUTPATH] [--use-filename] [--dump-stdout] [--html] [--pdf] [--wk-path WKPATH] [--wk-options [WKOPTIONS ...]]
[--prepared-html] [--charset CHARSET] [--raw] [--rtf] [--allow-fallback] [--skip-body-not-found] [--zip ZIP] [--save-header] [--attachments-only] [--skip-hidden] [--no-folders] [--skip-embedded] [--extract-embedded]
[--overwrite-existing] [--skip-not-implemented] [--out-name OUTNAME | --glob] [--ignore-rtfde] [--progress] [-s]
msg [msg ...]
usage: extract_msg [-h] [--use-content-id] [--json] [--file-logging] [-v] [--log LOG] [--config CONFIGPATH] [--out OUTPATH] [--use-filename] [--dump-stdout] [--html]
[--pdf] [--wk-path WKPATH] [--wk-options [WKOPTIONS ...]] [--wk-allow-external-files] [--prepared-html] [--charset CHARSET] [--raw] [--rtf]
[--allow-fallback] [--skip-body-not-found] [--zip ZIP] [--save-header] [--attachments-only] [--skip-hidden] [--no-folders] [--skip-embedded]
[--extract-embedded] [--overwrite-existing] [--skip-not-implemented] [--out-name OUTNAME | --glob] [--ignore-rtfde] [--progress] [-s]
[msg ...]

extract_msg: Extracts emails and attachments saved in Microsoft Outlook's .msg files. https://github.com/TeamMsgExtractor/msg-extractor

positional arguments:
msg An MSG file to be parsed.
msg An MSG file to be parsed.

options:
-h, --help show this help message and exit
--use-content-id, --cid
Save attachments by their Content ID, if they have one. Useful when working with the HTML body.
--json Changes to write output files as json.
--file-logging Enables file logging. Implies --verbose level 1.
-v, --verbose Turns on console logging. Specify more than once for higher verbosity.
--log LOG Set the path to write the file log to.
--config CONFIGPATH Set the path to load the logging config from.
--out OUTPATH Set the folder to use for the program output. (Default: Current directory)
--use-filename Sets whether the name of each output is based on the msg filename.
--dump-stdout Tells the program to dump the message body (plain text) to stdout. Overrides saving arguments.
--html Sets whether the output should be HTML. If this is not possible, will error.
--pdf Saves the body as a PDF. If this is not possible, will error.
--wk-path WKPATH Overrides the path for finding wkhtmltopdf.
--wk-options [WKOPTIONS ...]
Sets additional options to be used in wkhtmltopdf. Should be a series of options and values, replacing the - or -- in the beginning with + or ++, respectively. For example: --wk-options "+O Landscape"
--prepared-html When used in conjunction with --html, sets whether the HTML output should be prepared for embedded attachments.
--charset CHARSET Character set to use for the prepared HTML in the added tag. (Default: utf-8)
--raw Sets whether the output should be raw. If this is not possible, will error.
--rtf Sets whether the output should be RTF. If this is not possible, will error.
--allow-fallback Tells the program to fallback to a different save type if the selected one is not possible.
--skip-body-not-found
Skips saving the body if the body cannot be found, rather than throwing an error.
--zip ZIP Path to use for saving to a zip file.
--save-header Store the header in a separate file.
--attachments-only Specify to only save attachments from an msg file.
--skip-hidden Skips any attachment marked as hidden (usually ones embedded in the body).
--no-folders Stores everything in the location specified by --out. Requires --attachments-only and is incompatible with --out-name.
--skip-embedded Skips all embedded MSG files when saving attachments.
--extract-embedded Extracts the embedded MSG files as MSG files instead of running their save functions.
--overwrite-existing Disables filename conflict resolution code for attachments when saving a file, causing files to be overwriten if two attachments with the same filename are on an MSG file.
--skip-not-implemented, --skip-ni
Skips any attachments that are not implemented, allowing saving of the rest of the message.
--out-name OUTNAME Name to be used with saving the file output. Cannot be used if you are saving more than one file.
--glob, --wildcard Interpret all paths as having wildcards. Incompatible with --out-name.
--ignore-rtfde Ignores all errors thrown from RTFDE when trying to save. Useful for allowing fallback to continue when an exception happens.
--progress Shows what file the program is currently working on during it's progress.
-s, --stdin Read file from stdin (only works with one file at a time).
-h, --help show this help message and exit
--use-content-id, --cid
Save attachments by their Content ID, if they have one. Useful when working with the HTML body.
--json Changes to write output files as json.
--file-logging Enables file logging. Implies --verbose level 1.
-v, --verbose Turns on console logging. Specify more than once for higher verbosity.
--log LOG Set the path to write the file log to.
--config CONFIGPATH Set the path to load the logging config from.
--out OUTPATH Set the folder to use for the program output. (Default: Current directory)
--use-filename Sets whether the name of each output is based on the MSG filename.
--dump-stdout Tells the program to dump the message body (plain text) to stdout. Overrides saving arguments.
--html Sets whether the output should be HTML. If this is not possible, will error.
--pdf Saves the body as a PDF. If this is not possible, will error.
--wk-path WKPATH Overrides the path for finding wkhtmltopdf.
--wk-options [WKOPTIONS ...]
Sets additional options to be used in wkhtmltopdf. Should be a series of options and values, replacing the - or -- in the beginning with + or ++ respectively. For example: --wk-options "+O Landscape"
--wk-allow-external-files
Enables external files to appear in the PDF output. WARNING: This allows wkpdftohtml to make url requests that are unchecked, and as such may be a security vulnerability in untrusted emails. Only use this option if you trust the source of the MSG file.
--prepared-html When used in conjunction with --html, sets whether the HTML output should be prepared for embedded attachments.
--charset CHARSET Character set to use for the prepared HTML in the added tag. (Default: utf-8)
--raw Sets whether the output should be raw. If this is not possible, will error.
--rtf Sets whether the output should be RTF. If this is not possible, will error.
--allow-fallback Tells the program to fallback to a different save type if the selected one is not possible.
--skip-body-not-found
Skips saving the body if the body cannot be found, rather than throwing an error.
--zip ZIP Path to use for saving to a zip file.
--save-header Store the header in a separate file.
--attachments-only Specify to only save attachments from an MSG file.
--skip-hidden Skips any attachment marked as hidden (usually ones embedded in the body).
--no-folders Stores everything in the location specified by --out. Requires --attachments-only and is incompatible with --out-name.
--skip-embedded Skips all embedded MSG files when saving attachments.
--extract-embedded Extracts the embedded MSG files as MSG files instead of running their save functions.
--overwrite-existing Disables filename conflict resolution code for attachments when saving a file, causing files to be overwritten if two attachments with the same
filename are on an MSG file.
--skip-not-implemented, --skip-ni
Skips any attachments that are not implemented, allowing saving of the rest of the message.
--out-name OUTNAME Name to be used with saving the file output. Cannot be used if you are saving more than one file.
--glob, --wildcard Interpret all paths as having wildcards. Incompatible with --out-name.
--ignore-rtfde Ignores all errors thrown from RTFDE when trying to save. Useful for allowing fallback to continue when an exception happens.
--progress Shows what file the program is currently working on during it's progress.
-s, --stdin Read file from stdin (only works with one file at a time).

**To use this in your own script**, start by using:

Expand All @@ -122,7 +126,7 @@ From there, open the MSG file:
msg = extract_msg.openMsg("path/to/msg/file.msg")

Alternatively, if you wish to send a msg binary string instead of a file
to the ``extract_msg.openMsg`` Method:
to the ``extract_msg.openMsg()`` Method:

::

Expand Down Expand Up @@ -238,14 +242,18 @@ Credits

`Philippe Lagadec`_ - Python OleFile module developer.

`Seamus Tuohy`_ - Developer of the Python RTFDE module. Gave first examples of how to use the module and has worked with Destiny to ensure functionality.

`Kadhiraven EG`_ - Discovery of and assistance with vulnerability in PDF conversion, including assistance with updating security policies and notifying the public about the vulnerability.

`Joel Kaufman`_ - First implementations of the json and filename flags.

`Dean Malmgren`_ - First implementation of the setup.py script.

`Seamus Tuohy`_ - Developer of the Python RTFDE module. Gave first examples of how to use the module and has worked with Destiny to ensure functionality.

`Liam`_ - Significant reorganization and transfer of data.



And thank you to everyone who has opened an issue and helped us track down those pesky bugs.

Extra
Expand All @@ -260,12 +268,15 @@ your access to the newest major version of extract-msg.
.. |License: GPL v3| image:: https://img.shields.io/badge/License-GPLv3-blue.svg
:target: LICENSE.txt

.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.56.1-blue.svg
:target: https://pypi.org/project/extract-msg/0.56.1/
.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.57.0-blue.svg
:target: https://pypi.org/project/extract-msg/0.57.0/

.. |PyPI2| image:: https://img.shields.io/badge/python-3.8+-brightgreen.svg
:target: https://www.python.org/downloads/release/python-3810/

.. |Downloads| image:: https://img.shields.io/pypi/dm/extract-msg.svg
:target: https://pypistats.org/packages/extract-msg

.. |Read the Docs| image:: https://readthedocs.org/projects/msg-extractor/badge/?version=latest
:target: https://msg-extractor.readthedocs.io/en/stable/?badge=latest
:alt: Documentation Status
Expand All @@ -277,6 +288,7 @@ your access to the newest major version of extract-msg.
.. _Dean Malmgren: https://github.com/deanmalmgren
.. _Joel Kaufman: https://github.com/joelkaufman
.. _Liam: https://github.com/LiamPM5
.. _Kadhiraven EG: https://github.com/JOKER-53
.. _Seamus Tuohy: https://github.com/seamustuohy
.. _Discord: https://discord.com/invite/B77McRmzdc
.. _Buy Me a Coffee: https://www.buymeacoffee.com/DestructionE
Expand Down
Binary file added example-msg-files/multi-stream.msg
Binary file not shown.
Binary file modified example-msg-files/multi-to-to.msg
Binary file not shown.
Binary file modified example-msg-files/multi-to.msg
Binary file not shown.
Loading
Loading