Commit Graph

46 Commits

Author SHA1 Message Date
Sean Whalen 5b08627eaa Split tests.py into per-module tests/test_<module>.py (#774)
* Split tests.py into per-module tests/test_<module>.py

The 5174-line tests.py monolith is split into per-module files under
tests/, mirroring the checkdmarc layout:

  tests/test_init.py          parsedmarc/__init__.py parsing surface
  tests/test_cli.py           parsedmarc/cli.py + config / env-vars / SIGHUP
  tests/test_utils.py         parsedmarc/utils.py (DNS, IP info, PSL, etc.)
  tests/test_webhook.py       parsedmarc/webhook.py
  tests/test_kafkaclient.py   parsedmarc/kafkaclient.py
  tests/test_splunk.py        parsedmarc/splunk.py
  tests/test_syslog.py        parsedmarc/syslog.py
  tests/test_loganalytics.py  parsedmarc/loganalytics.py
  tests/test_gelf.py          parsedmarc/gelf.py
  tests/test_s3.py            parsedmarc/s3.py
  tests/test_maps.py          parsedmarc/resources/maps/ maintainer scripts

The split is purely a redistribution — no test bodies changed, no tests
added or removed. All 276 existing tests pass under the new layout.

The current tests.py contains two kitchen-sink classes (`Test` at line 54
and `TestEnvVarConfig` at line 2360) holding tests that span many
modules. Their methods are routed to the correct per-module file by name
prefix; the wholly-thematic classes (TestExtractReport, TestUtilsXxx,
TestSighupReload, etc.) move whole. Each target file gets its own
`class Test(unittest.TestCase)` for the redistributed kitchen-sink
methods, plus the thematic classes verbatim.

Wiring updates:
- `.github/workflows/python-tests.yml`: `pytest ... tests.py` →
  `python -m pytest ... tests/` (also switches to `python -m pytest` per
  the checkdmarc convention so cwd lands on the project root).
- `pyproject.toml`: adds `[tool.pytest.ini_options] testpaths = ["tests"]`
  and `[tool.coverage.run] source = ["parsedmarc"]` with an `omit` for
  `parsedmarc/resources/maps/*.py`. The maps scripts are maintainer-only
  batch tooling that ships out of the wheel; excluding them from
  coverage makes the headline number reflect only installed library
  code. Runtime coverage on the new layout is 59% (was 45% with maps
  counted), and PR-B will push it to 90%+.
- `AGENTS.md`: documents the new layout and how to run individual files
  / tests; tells future contributors not to reintroduce a monolithic
  tests.py.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Restore 66.9% coverage baseline (count tests/ + parsedmarc)

Master's headline 66.9% number on Codecov includes the tests.py file
itself (99.35% covered) being measured alongside parsedmarc/*.  The
original tests.py had no `[tool.coverage.run]` block, so coverage's
default — "measure every file imported during the run" — counted the
test code as if it were product code.

The split commit added `source = ["parsedmarc"]` which suppressed
measurement of the test files (correct in principle, since test files
aren't shipped code), and that alone made the headline number drop by
~8 percentage points without any actual loss of testing.  This commit
swaps `source` for an explicit `include = ["parsedmarc/*", "tests/*"]`
so both halves are measured the way they were on master.  Verified:
276 tests, 66.96% line coverage (effectively unchanged from master's
66.90%).

If you want the shipped-code-only number (was the headline that this
commit overrides), run `pytest --cov=parsedmarc tests/`.  That number
is currently 59% and is the focus of the upcoming coverage-expansion PR.

Also adds junit.xml to .gitignore so the CI artefact doesn't get
accidentally committed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Restrict coverage to shipped code (`source = ["parsedmarc"]`)

Reverts the prior commit's `include = ["tests/*"]`. Counting the test
files toward coverage was wrong — it conflates "shipped code exercised
by tests" with "test code that pytest auto-runs", inflates the headline
number, and rewards writing more tests rather than tests that verify
more code. Master's apparent 66.9% was an artefact of the old
monolithic tests.py having no [tool.coverage.run] block at all; coverage's
default behaviour measured every imported file, including the test file
itself at ~99% "covered", which added ~8 percentage points to the
displayed number without any real testing signal.

Restricting to `source = ["parsedmarc"]` plus the existing maps omit
gives a meaningful baseline: 59% of shipped code is exercised by the
test suite today. That's the number the next PR is targeting to lift
to 90%+ before the 10.0.0 release; the Codecov "drop" here is a
measurement correction, not a regression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 19:29:09 -04:00
Copilot ae1e5adb66 Add RFC 9989/9990/9991 (final DMARC) report support; rename forensic→failure project-wide (#659)
* Add DMARCbis report support; rename forensic→failure project-wide

Rebased on top of master @ 2cda5bf (9.9.0), which added the ASN
source attribution work (#712, #713, #714, #715). Individual Copilot
iteration commits squashed into this single commit — the per-commit
history on the feature branch was iterative (add tests, fix lint,
move field, revert, etc.) and not worth preserving; GitHub squash-
merges PRs anyway.

New fields from the DMARCbis XSD, plumbed through types, parsing, CSV
output, and the Elasticsearch / OpenSearch mappings:

- ``np`` — non-existent subdomain policy (``none`` / ``quarantine`` /
  ``reject``)
- ``testing`` — testing mode flag (``n`` / ``y``), replaces RFC 7489
  ``pct``
- ``discovery_method`` — policy discovery method (``psl`` /
  ``treewalk``)
- ``generator`` — report generator software identifier (metadata)
- ``human_result`` — optional descriptive text on DKIM / SPF results

RFC 7489 reports parse with ``None`` for DMARCbis-only fields.

Forensic reports have been renamed to failure reports throughout the
project to reflect the proper naming since RFC 7489.

- Core: ``types.py``, ``__init__.py`` — ``ForensicReport`` →
  ``FailureReport``, ``parse_forensic_report`` →
  ``parse_failure_report``, report type ``"failure"``.
- Output modules: ``elastic.py``, ``opensearch.py``, ``splunk.py``,
  ``kafkaclient.py``, ``syslog.py``, ``gelf.py``, ``webhook.py``,
  ``loganalytics.py``, ``s3.py``.
- CLI: ``cli.py`` — args, config keys, index names
  (``dmarc_failure``).
- Docs + dashboards: all markdown, Grafana JSON, Kibana NDJSON,
  Splunk XML.

Backward compatibility preserved: old function / type names remain as
aliases (``parse_forensic_report = parse_failure_report``,
``ForensicReport = FailureReport``, etc.), CLI accepts both the old
(``save_forensic``, ``forensic_topic``) and new (``save_failure``,
``failure_topic``) config keys, and updated dashboards query both
old and new index / sourcetype names so data from before and after
the rename appears together.

Merge conflicts resolved in ``parsedmarc/constants.py`` (took bis's
10.0.0 bump), ``parsedmarc/__init__.py`` (combined bis's "failure"
wording with master's IPinfo MMDB mention), ``parsedmarc/elastic.py``
and ``parsedmarc/opensearch.py`` (kept master's ``source_asn`` /
``source_asn_name`` / ``source_asn_domain`` on the failure doc path
while renaming ``forensic_report`` → ``failure_report``), and
``CHANGELOG.md`` (10.0.0 entry now sits above the 9.9.0 entry).

All 324 tests pass; ``ruff check`` / ``ruff format --check`` clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Apply post-RFC review fixes: RFC 9990 detection, langAttrString, CFWS-aware RUF parsing

Aligns the implementation with the final RFCs (9989/9990/9991) instead of
inferring DMARCbis support from the version element or the namespace alone.

Aggregate parsing (RFC 9990):
- _text() helper unwraps langAttrString values (extra_contact_info, error,
  comment, human_result, generator) — when reporters include the lang
  attribute, xmltodict yields {"#text": ..., "@lang": ...} dicts instead
  of strings; the parser now stores the text payload in both shapes.
- New xml_namespace field on AggregateReport records the declared XML
  namespace (urn:ietf:params:xml:ns:dmarc-2.0 for RFC 9990 reports).
- RFC 9990 detection accepts namespaceless reports that follow the
  RFC 9990 shape (presence of np / testing / discovery_method / generator),
  so reporters that don't declare the namespace still receive RFC 9990-
  aware validation.
- Warnings: missing DKIM <selector> (REQUIRED in RFC 9990); legacy
  forwarded / sampled_out policy-override types (removed by RFC 9990);
  unknown policy-override types per the RFC 9990 enumeration.
- xml_namespace added to Elasticsearch and OpenSearch document mappings.

Failure parsing (RFC 9991):
- Identity-Alignment and Auth-Failure are split on commas with CFWS
  whitespace stripped per the RFC 9991 ABNF; previously "dkim, spf"
  yielded ["dkim", " spf"] with a leading space on the second token.
- Warnings logged when either REQUIRED field is missing.

Terminology: every reference to "DMARCbis" in code, tests, sample
filenames, AGENTS.md, and CHANGELOG.md is replaced with the appropriate
RFC number (9989 for the policy spec, 9990 for aggregate reports, 9991
for failure reports). Sample contents are unchanged.

Docs: corrects the prior claim that fo was dropped from RFC 9990 (only
pct was), reframes testing as a new field (not a pct replacement, since
RFC 9989 Appendix A.6 removed pct with no per-message substitute), and
documents the policy_override_reason enum changes (added policy_test_mode;
removed forwarded / sampled_out).

Tests: 8 new tests covering xml_namespace capture, RFC 9990 detection
from field shape, missing-DKIM-selector warning, legacy-override-type
warning, langAttrString unwrapping across all four affected elements,
and CFWS-aware Identity-Alignment / Auth-Failure parsing plus their
missing-field warnings. 276 tests total, all passing; ruff clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sean Whalen <44679+seanthegeek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 18:51:08 -04:00
Sean Whalen 8c5f63620c Fix Validate-dashboards CI: heredoc was redirecting itself to stdin (#773)
`echo "$response" | python3 - <<'PY' ... PY` redirected the heredoc
to python3's stdin (where it was correctly read as the script body), but
sys.stdin was then at EOF when the script called json.load(sys.stdin) —
so the assertion blew up with 'Expecting value: line 1 column 1' even
when Kibana's import had succeeded.

Pass the response via env var instead. The OSD ndjson import itself was
working all along (successCount: 26, success: true); only the assertion
step was broken, so master has been showing a red Validate-dashboards
run since the workflow was introduced.
2026-05-20 09:38:15 -04:00
Sean Whalen 2d3e896f6d Fix pytest command line argument typo 2026-05-19 21:17:47 -04:00
Sean Whalen c5b2fcec54 Enhance CI with JUnit XML output and Codecov results
Added JUnit XML output for pytest and Codecov test results upload.
2026-05-19 21:12:20 -04:00
Sean Whalen 4e8c28bbc0 Align Kibana dashboards with OpenSearch Dashboards source-of-truth (#737)
* Align Kibana dashboards with OpenSearch Dashboards source-of-truth

OSD is a fork of Kibana 7.10 and Kibana 8.x's saved-object migration
handlers accept OSD's saved-object format directly. Replace the legacy
Kibana export with a byte-identical copy of the OSD ndjson, so the two
backends ship the same panels, metric aggregations, panel titles, and
field assignments instead of drifting independently.

Verified against Kibana 8.19.7: import returns successCount=26 with no
errors and Kibana auto-migrates each viz / dashboard to its current
saved-object schema (typeMigrationVersion 8.5.0 for visualizations,
10.3.0 for dashboards) on import.

Net effects for Kibana users on import:

- Picks up the metric-aggregation fix from 9.10.3 — pies, tables, and
  the choropleth now sum(message_count) instead of counting OS docs,
  giving real message volume rather than distinct source-row counts.
- Adds "Message sources by Autonomous System" and "Message sources by
  name and type" panels (previously only on OSD).
- Forensic dashboard simplified to OSD's two-panel layout (markdown
  intro + samples table) — drops the Kibana-only IP-address and
  country-ISO tables and the choropleth.
- Adds the "SMTP TLS reporting" dashboard (was absent from the bundled
  Kibana export).
- Drops the extraneous "Evolution DMARC par source_reverse_DNS" Lens
  visualization that snuck in via a community contribution.

Updates docs/source/kibana.md to reflect the new dashboard names
("DMARC aggregate reports" / "DMARC failure reports") and adds a brief
section on the SMTP TLS reporting dashboard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Drop the duplicate Kibana ndjson; point Kibana users at the OSD file

Kibana 8.x's saved-object migration handlers accept the OpenSearch
Dashboards saved-object format directly (verified by import returning
successCount=26 with no errors), so a separate kibana/export.ndjson
was just two copies of the same bytes that would inevitably drift. Drop
it and update the bootstrap script and docs to point at the existing
dashboards/opensearch/opensearch_dashboards.ndjson.

Add a path-filtered CI workflow (.github/workflows/dashboards.yml) that
fires only when the OSD ndjson changes. It stands up an Elasticsearch +
Kibana 8.19.7 service pair, POSTs the file at the saved-objects import
endpoint, and asserts success=true with no errors. That keeps the
single-file source compatible with Kibana on every change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Sean Whalen <seanthegeek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 01:30:48 -04:00
Sean Whalen 5785cb2072 Add weekly workflow to refresh the bundled IPinfo Lite MMDB (#718)
Runs Mondays at 06:00 UTC (and on workflow_dispatch), downloads the
latest MMDB using an IPINFO_TOKEN secret, validates it with a sample
lookup, and opens a PR if the file changed.

Co-authored-by: Sean Whalen <seanthegeek@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:25:03 -04:00
Kili 298d5b6e6e CI: split lint/docs/build from integration tests matrix (#669)
* Optimize CI: split lint/docs/build from integration tests

* Trim unnecessary package install from lint job
2026-03-09 17:09:02 -04: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
Anael Mobilia 50fcb51577 Update supported Python versions in docs + readme (#652)
* Update README.md

* Update index.md

* Update python-tests.yml
2026-01-19 14:40:01 -05:00
Sean Whalen 4b786846ae Remove Python 3.14 from testing
Until cpython bug https://github.com/python/cpython/issues/142307 is fixed
2025-12-05 11:05:29 -05:00
Sean Whalen 681b7cbf85 Formatting 2025-12-01 10:56:08 -05:00
Anael Mobilia a51f945305 Clearly define supported Python versions policy (#633)
* Clearly define supported Python versions.

Support policy based on author's comment on https://github.com/domainaware/parsedmarc/pull/458#issuecomment-2002516299 #458

* Compile Python 3.6 as Ubuntu latest run against Ubuntu 24.04 which haven't Python3.6 + 20.04 is no longer available
https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json

* Use latest versions of GH Actions

* Silent some technicals GH Actions steps

* Elasticsearch / opensearch: use supported versions + align used versions

* Delete .github/workflows/python-tests-3.6.yml

Drop Python 3.6 test

* Update Python 3.6 support status in README

---------

Co-authored-by: Sean Whalen <44679+seanthegeek@users.noreply.github.com>
2025-12-01 10:02:47 -05:00
Rowan 8a299b8600 Updated default python docker base image to 3.13-slim (#618)
* Updated default python docker base image to 3.13-slim

* Added python 3.13 to tests
2025-10-29 22:34:06 -04:00
Szasza Palmer e299f7d161 fixing ES/OS forensic report lookup and storage, extracting ES to separate CI service (#603)
* fixing ES/OS forensic report lookup and storage, extracting ES to separate CI service

* bumping CI ES version to current latest

* reshuffling CI job attributes

* removing EOL Python 3.8 from the CI pipeline
2025-06-02 11:10:10 -04:00
Jed Laundry 8444053476 Create optional dependency group for build, fix codecov (#567)
* Create optional dependency groups for build and cli

* revert cli optional-dependencies group
2024-10-07 13:47:35 -04:00
Sean Whalen 1ef3057110 8.15.1
- Proper IMAP namespace fix (Closes issue #557 and issue #563)
  - Require `mailsuite>=1.9.17`
  - Revert PR #552
- Add pre-flight check for nameservers (PR #562 closes issue #543)
- Reformat code with `ruff`
2024-10-02 21:19:57 -04:00
Sean Whalen 5973ca8205 Code style fixes 2024-09-04 16:48:07 -04:00
yuji suzuki e6e282a10c Fix ci and gha update (#526)
* add gmail_api example to README.md

* fix ci

fix

fix

m

fix

* codecov update
2024-05-28 16:54:17 -04:00
yuji suzuki 201280d700 Fixed ci for Test sample DMARC reports (#520)
* Fixed ci for Test sample DMARC reports

* pip install local local folder

* pip install path fix
2024-05-22 08:14:04 -04:00
yuji suzuki c150c7671f github actions run in all python versions and ci version up (#519) 2024-05-22 08:12:55 -04:00
Anael Mobilia 68145b8b06 setup.py don't exist anymore (#434)
setup.py file was deleted on https://github.com/domainaware/parsedmarc/commit/de3002db8bf8b63a43635cd6d2b506d02eb139fd
2023-10-11 17:25:25 -04:00
Anael Mobilia aaf269b11b Install latest elasticsearch for tests (#433) 2023-10-11 17:23:19 -04:00
Sean Whalen be4c236d69 Use hatch when testing building packages 2022-09-10 14:43:15 -04:00
Sean Whalen 4376b12c93 Remove sstcheck from tests 2022-09-10 14:39:08 -04:00
Sean Whalen 12e591255c Fix tests 2022-09-10 14:32:43 -04:00
Sean Whalen 6ccc827e4c Fix Python tests in GitHub 2022-09-10 14:16:17 -04:00
William Desportes 9381381f40 CI improvements (#327)
* Add permissions on GitHub workflows tokens to avoid misuse and leak

* Bump docker actions

* Bump elasticsearch version
2022-06-20 09:49:43 -04:00
Nathan Thorpe abf07125c4 Add Dockerfile & build/push task (#316)
* add dockerfile and actions task to build image

* test on branch

* change to push only on release, update readme

* remove pip install requirements

* change to on release github action
2022-05-05 21:06:38 -04:00
Sean Whalen 2b55c9ec13 Update python-tests.yml 2022-01-05 12:11:35 -05:00
Sean Whalen dcdc210ab6 PEP8 style fixes 2022-01-05 10:59:00 -05:00
Sean Whalen 8259f6866f Update python-tests.yml 2022-01-05 10:46:31 -05:00
Sean Whalen 4f22ab4879 Update python-tests.yml 2022-01-05 10:36:29 -05:00
Sean Whalen ce2943e0cc Update python-tests.yml 2022-01-05 10:29:58 -05:00
Sean Whalen c0f82fa363 Update Github Workflows 2022-01-05 10:29:19 -05:00
Sean Whalen fcf5a3be31 Update python-tests.yml 2022-01-05 09:54:55 -05:00
Sean Whalen c0e975b1e0 Update python-tests.yml 2022-01-05 09:45:58 -05:00
Sean Whalen d50259cdc3 Update python-tests.yml 2022-01-05 09:44:14 -05:00
Sean Whalen 8a5242be5a Update python-tests.yml 2022-01-05 09:43:22 -05:00
Sean Whalen ddb02cd031 Update python-tests.yml 2022-01-05 09:31:26 -05:00
Sean Whalen 273e9906a3 Update python-tests.yml 2022-01-05 09:30:12 -05:00
Sean Whalen ca2c18b566 Update python-tests.yml 2021-12-07 12:19:53 -05:00
Sean Whalen 25b63e484c Update python-tests.yml 2021-12-07 12:14:33 -05:00
Sean Whalen de3ac64583 Update python-tests.yml 2021-12-07 12:02:16 -05:00
Sean Whalen 76a7a47c53 Update python-tests.yml 2021-12-07 10:55:05 -05:00
Sean Whalen 2748022824 Create python-tests.yml 2021-12-07 10:51:36 -05:00