Compare commits

...

6 Commits

Author SHA1 Message Date
Kili
e98fdfa96b Fix Python 3.14 support metadata and require imapclient 3.1.0 (#662) 2026-03-04 12:36:15 -05:00
Sean Whalen
9551c8b467 Add AGENTS.md for AI agent guidance and link from CLAUDE.md 2026-03-03 21:00:55 -05:00
Sean Whalen
d987943c22 Update changelog formatting for version 9.1.1 2026-03-03 11:46:13 -05:00
Sean Whalen
3d8a99b5d3 9.1.1
- Fix the use of Elasticsearch and OpenSearch API keys (PR #660 fixes issue #653)
- Drop support for Python 3.9 (PR #661)
2026-03-03 11:43:53 -05:00
Majid Burney
5aaaedf463 Use correct key names for elasticsearch/opensearch api keys (#660) 2026-03-03 11:35:05 -05:00
Copilot
2e3ee25ec9 Drop Python 3.9 support (#661)
* Initial plan

* Drop Python 3.9 support: update CI matrix, pyproject.toml, docs, and README

Co-authored-by: seanthegeek <44679+seanthegeek@users.noreply.github.com>

* Update Python 3.9 version table entry to note Debian 11/RHEL 9 usage

Co-authored-by: seanthegeek <44679+seanthegeek@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: seanthegeek <44679+seanthegeek@users.noreply.github.com>
2026-03-03 11:34:35 -05:00
11 changed files with 93 additions and 16 deletions

View File

@@ -30,7 +30,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5

64
AGENTS.md Normal file
View File

@@ -0,0 +1,64 @@
# AGENTS.md
This file provides guidance to AI agents when working with code in this repository.
## Project Overview
parsedmarc is a Python module and CLI utility for parsing DMARC aggregate (RUA), forensic (RUF), and SMTP TLS reports. It reads reports from IMAP, Microsoft Graph, Gmail API, Maildir, mbox files, or direct file paths, and outputs to JSON/CSV, Elasticsearch, OpenSearch, Splunk, Kafka, S3, Azure Log Analytics, syslog, or webhooks.
## Common Commands
```bash
# Install with dev/build dependencies
pip install .[build]
# Run all tests with coverage
pytest --cov --cov-report=xml tests.py
# Run a single test
pytest tests.py::Test::testAggregateSamples
# Lint and format
ruff check .
ruff format .
# Test CLI with sample reports
parsedmarc --debug -c ci.ini samples/aggregate/*
parsedmarc --debug -c ci.ini samples/forensic/*
# Build docs
cd docs && make html
# Build distribution
hatch build
```
To skip DNS lookups during testing, set `GITHUB_ACTIONS=true`.
## Architecture
**Data flow:** Input sources → CLI (`cli.py:_main`) → Parse (`__init__.py`) → Enrich (DNS/GeoIP via `utils.py`) → Output integrations
### Key modules
- `parsedmarc/__init__.py` — Core parsing logic. Main functions: `parse_report_file()`, `parse_report_email()`, `parse_aggregate_report_xml()`, `parse_forensic_report()`, `parse_smtp_tls_report_json()`, `get_dmarc_reports_from_mailbox()`, `watch_inbox()`
- `parsedmarc/cli.py` — CLI entry point (`_main`), config file parsing, output orchestration
- `parsedmarc/types.py` — TypedDict definitions for all report types (`AggregateReport`, `ForensicReport`, `SMTPTLSReport`, `ParsingResults`)
- `parsedmarc/utils.py` — IP/DNS/GeoIP enrichment, base64 decoding, compression handling
- `parsedmarc/mail/` — Polymorphic mail connections: `IMAPConnection`, `GmailConnection`, `MSGraphConnection`, `MaildirConnection`
- `parsedmarc/{elastic,opensearch,splunk,kafkaclient,loganalytics,syslog,s3,webhook,gelf}.py` — Output integrations
### Report type system
`ReportType = Literal["aggregate", "forensic", "smtp_tls"]`. Exception hierarchy: `ParserError``InvalidDMARCReport``InvalidAggregateReport`/`InvalidForensicReport`, and `InvalidSMTPTLSReport`.
### Caching
IP address info cached for 4 hours, seen aggregate report IDs cached for 1 hour (via `ExpiringDict`).
## Code Style
- Ruff for formatting and linting (configured in `.vscode/settings.json`)
- TypedDict for structured data, type hints throughout
- Python ≥3.10 required
- Tests are in a single `tests.py` file using unittest; sample reports live in `samples/`

View File

@@ -1,5 +1,15 @@
# Changelog # Changelog
## 9.1.1
### Fixes
- Fix the use of Elasticsearch and OpenSearch API keys (PR #660 fixes issue #653)
### Changes
- Drop support for Python 3.9 (PR #661)
## 9.1.0 ## 9.1.0
## Enhancements ## Enhancements

3
CLAUDE.md Normal file
View File

@@ -0,0 +1,3 @@
# CLAUD.md
@AGENTS.md

View File

@@ -56,9 +56,9 @@ for RHEL or Debian.
| 3.6 | ❌ | Used in RHEL 8, but not supported by project dependencies | | 3.6 | ❌ | Used in RHEL 8, but not supported by project dependencies |
| 3.7 | ❌ | End of Life (EOL) | | 3.7 | ❌ | End of Life (EOL) |
| 3.8 | ❌ | End of Life (EOL) | | 3.8 | ❌ | End of Life (EOL) |
| 3.9 | | Supported until August 2026 (Debian 11); May 2032 (RHEL 9) | | 3.9 | | Used in Debian 11 and RHEL 9, but not supported by project dependencies |
| 3.10 | ✅ | Actively maintained | | 3.10 | ✅ | Actively maintained |
| 3.11 | ✅ | Actively maintained; supported until June 2028 (Debian 12) | | 3.11 | ✅ | Actively maintained; supported until June 2028 (Debian 12) |
| 3.12 | ✅ | Actively maintained; supported until May 2035 (RHEL 10) | | 3.12 | ✅ | Actively maintained; supported until May 2035 (RHEL 10) |
| 3.13 | ✅ | Actively maintained; supported until June 2030 (Debian 13) | | 3.13 | ✅ | Actively maintained; supported until June 2030 (Debian 13) |
| 3.14 | ✅ | Actively maintained | | 3.14 | ✅ | Supported (requires `imapclient>=3.1.0`) |

View File

@@ -56,12 +56,12 @@ for RHEL or Debian.
| 3.6 | ❌ | Used in RHEL 8, but not supported by project dependencies | | 3.6 | ❌ | Used in RHEL 8, but not supported by project dependencies |
| 3.7 | ❌ | End of Life (EOL) | | 3.7 | ❌ | End of Life (EOL) |
| 3.8 | ❌ | End of Life (EOL) | | 3.8 | ❌ | End of Life (EOL) |
| 3.9 | | Supported until August 2026 (Debian 11); May 2032 (RHEL 9) | | 3.9 | | Used in Debian 11 and RHEL 9, but not supported by project dependencies |
| 3.10 | ✅ | Actively maintained | | 3.10 | ✅ | Actively maintained |
| 3.11 | ✅ | Actively maintained; supported until June 2028 (Debian 12) | | 3.11 | ✅ | Actively maintained; supported until June 2028 (Debian 12) |
| 3.12 | ✅ | Actively maintained; supported until May 2035 (RHEL 10) | | 3.12 | ✅ | Actively maintained; supported until May 2035 (RHEL 10) |
| 3.13 | ✅ | Actively maintained; supported until June 2030 (Debian 13) | | 3.13 | ✅ | Actively maintained; supported until June 2030 (Debian 13) |
| 3.14 | ✅ | Actively maintained | | 3.14 | ✅ | Supported (requires `imapclient>=3.1.0`) |
```{toctree} ```{toctree}
:caption: 'Contents' :caption: 'Contents'

View File

@@ -162,10 +162,10 @@ sudo -u parsedmarc virtualenv /opt/parsedmarc/venv
``` ```
CentOS/RHEL 8 systems use Python 3.6 by default, so on those systems CentOS/RHEL 8 systems use Python 3.6 by default, so on those systems
explicitly tell `virtualenv` to use `python3.9` instead explicitly tell `virtualenv` to use `python3.10` instead
```bash ```bash
sudo -u parsedmarc virtualenv -p python3.9 /opt/parsedmarc/venv sudo -u parsedmarc virtualenv -p python3.10 /opt/parsedmarc/venv
``` ```
Activate the virtualenv Activate the virtualenv

View File

@@ -1058,10 +1058,10 @@ def _main():
opts.elasticsearch_password = elasticsearch_config["password"] opts.elasticsearch_password = elasticsearch_config["password"]
# Until 8.20 # Until 8.20
if "apiKey" in elasticsearch_config: if "apiKey" in elasticsearch_config:
opts.elasticsearch_apiKey = elasticsearch_config["apiKey"] opts.elasticsearch_api_key = elasticsearch_config["apiKey"]
# Since 8.20 # Since 8.20
if "api_key" in elasticsearch_config: if "api_key" in elasticsearch_config:
opts.elasticsearch_apiKey = elasticsearch_config["api_key"] opts.elasticsearch_api_key = elasticsearch_config["api_key"]
if "opensearch" in config: if "opensearch" in config:
opensearch_config = config["opensearch"] opensearch_config = config["opensearch"]
@@ -1098,10 +1098,10 @@ def _main():
opts.opensearch_password = opensearch_config["password"] opts.opensearch_password = opensearch_config["password"]
# Until 8.20 # Until 8.20
if "apiKey" in opensearch_config: if "apiKey" in opensearch_config:
opts.opensearch_apiKey = opensearch_config["apiKey"] opts.opensearch_api_key = opensearch_config["apiKey"]
# Since 8.20 # Since 8.20
if "api_key" in opensearch_config: if "api_key" in opensearch_config:
opts.opensearch_apiKey = opensearch_config["api_key"] opts.opensearch_api_key = opensearch_config["api_key"]
if "splunk_hec" in config.sections(): if "splunk_hec" in config.sections():
hec_config = config["splunk_hec"] hec_config = config["splunk_hec"]

View File

@@ -1,3 +1,3 @@
__version__ = "9.1.0" __version__ = "9.1.1"
USER_AGENT = f"parsedmarc/{__version__}" USER_AGENT = f"parsedmarc/{__version__}"

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from typing import Any, Dict, List, Literal, Optional, TypedDict, Union from typing import Any, Dict, List, Literal, Optional, TypedDict, Union
# NOTE: This module is intentionally Python 3.9 compatible. # NOTE: This module is intentionally Python 3.10 compatible.
# - No PEP 604 unions (A | B) # - No PEP 604 unions (A | B)
# - No typing.NotRequired / Required (3.11+) to avoid an extra dependency. # - No typing.NotRequired / Required (3.11+) to avoid an extra dependency.
# For optional keys, use total=False TypedDicts. # For optional keys, use total=False TypedDicts.

View File

@@ -2,7 +2,7 @@
requires = [ requires = [
"hatchling>=1.27.0", "hatchling>=1.27.0",
] ]
requires_python = ">=3.9,<3.14" requires_python = ">=3.10,<3.15"
build-backend = "hatchling.build" build-backend = "hatchling.build"
[project] [project]
@@ -29,7 +29,7 @@ classifiers = [
"Operating System :: OS Independent", "Operating System :: OS Independent",
"Programming Language :: Python :: 3" "Programming Language :: Python :: 3"
] ]
requires-python = ">=3.9" requires-python = ">=3.10"
dependencies = [ dependencies = [
"azure-identity>=1.8.0", "azure-identity>=1.8.0",
"azure-monitor-ingestion>=1.0.0", "azure-monitor-ingestion>=1.0.0",
@@ -45,7 +45,7 @@ dependencies = [
"google-auth-httplib2>=0.1.0", "google-auth-httplib2>=0.1.0",
"google-auth-oauthlib>=0.4.6", "google-auth-oauthlib>=0.4.6",
"google-auth>=2.3.3", "google-auth>=2.3.3",
"imapclient>=2.1.0", "imapclient>=3.1.0",
"kafka-python-ng>=2.2.2", "kafka-python-ng>=2.2.2",
"lxml>=4.4.0", "lxml>=4.4.0",
"mailsuite>=1.11.2", "mailsuite>=1.11.2",