Email-Abuse-Investigator

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

Use `report()` for human review or ticketing systems.  Use
`abuse_report_text()` for sending to ISP abuse desks.

### Usage

    print $analyser->report();

    open my $fh, '>', 'report.txt' or croak "Cannot open: $!";
    print $fh $analyser->report();
    close $fh;

### Arguments

None.  `parse_email()` must have been called first.

### Returns

A plain scalar string, newline-terminated, Unix line endings.  Never empty
or undef.

### Side Effects

Triggers all analysis methods if not already cached.

### Notes

The report is idempotent: calling it multiple times on the same object
always returns an identical string.  All user-derived content is sanitised
before output.

### API Specification

#### Input

    []

#### Output

    { type => 'string' }

## header\_value( $name )

Returns the value of the first occurrence of a named header field, or
`undef` if the header is absent.  The name comparison is case-insensitive.

### Usage

    my $subj = $analyser->header_value('Subject');
    my $from  = $analyser->header_value('From');
    my $msgid = $analyser->header_value('Message-ID');

### Arguments

- `$name` (string, required)

    The header field name, e.g. `'Subject'`, `'From'`, `'X-Mailer'`.
    Comparison is case-insensitive.

### Returns

The raw header value string (not decoded), or `undef` if the named header
is not present.  When the same header appears more than once, only the value
of the first occurrence is returned.

### Side Effects

None.  Header data is pre-parsed during `parse_email()`.

### Notes

Header values are returned verbatim, including any MIME encoded-word sequences
(`=?charset?B/Q?...?=`).  Pass the result through `_decode_mime_words()`
internally if human-readable output is needed.

### API Specification

#### Input

    {
        name => { type => 'string', required => 1 },
    }

#### Output

    { type => [ 'string', 'undef' ] }

### Messages

None -- returns `undef` on a missing header, never throws.

# ALGORITHM: DOMAIN INTELLIGENCE PIPELINE

For each unique non-infrastructure domain found in the email, the module
runs the following pipeline:

    Domain name
        |
        +-- A/AAAA record --> web hosting IP --> RDAP --> org + abuse contact
        |
        +-- MX record --> mail server hostname --> A --> RDAP --> org + abuse
        |
        +-- NS record --> nameserver hostname  --> A --> RDAP --> org + abuse
        |
        +-- WHOIS (TLD whois server via IANA referral)
               +-- Registrar name + abuse contact
               +-- Creation date  (-> recently-registered flag if < 180 days)
               +-- Expiry date    (-> expires-soon or expired flags)

Domains are collected from:

    From:/Reply-To:/Sender:/Return-Path: headers
    DKIM-Signature: d=  (signing domain)
    List-Unsubscribe:   (ESP / bulk sender domain)
    Message-ID:         (often reveals real sending platform)
    mailto: links and bare addresses in the body

# CACHING

Two levels of caching are used:

- Per-message cache (`$self->{_domain_info}`)

README.md  view on Meta::CPAN

# SUPPORT

This module is provided as-is without any warranty.

Please report any bugs or feature requests to `bug-email-abuse-investigator at rt.cpan.org`,
or through the web interface at
[http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Email-Abuse-Investigator](http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Email-Abuse-Investigator).
I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

You can find documentation for this module with the perldoc command.

    perldoc Email::Abuse::Investigator

You can also look for information at:

- MetaCPAN

    [https://metacpan.org/dist/Email-Abuse-Investigator](https://metacpan.org/dist/Email-Abuse-Investigator)

- RT: CPAN's request tracker

    [https://rt.cpan.org/NoAuth/Bugs.html?Dist=Email-Abuse-Investigator](https://rt.cpan.org/NoAuth/Bugs.html?Dist=Email-Abuse-Investigator)

- CPAN Testers' Matrix

    [http://matrix.cpantesters.org/?dist=Email-Abuse-Investigator](http://matrix.cpantesters.org/?dist=Email-Abuse-Investigator)

- CPAN Testers Dependencies

    [http://deps.cpantesters.org/?module=Email-Abuse-Investigator](http://deps.cpantesters.org/?module=Email-Abuse-Investigator)

# REQUIRED MODULES

The following modules are mandatory:

    Readonly::Values::Months
    Socket              (core since Perl 5)
    IO::Socket::INET    (core since Perl 5)
    MIME::QuotedPrint   (core since Perl 5.8)
    MIME::Base64        (core since Perl 5.8)

The following are optional but strongly recommended:

    Net::DNS            -- enables MX, NS, AAAA record lookups
    LWP::UserAgent      -- enables RDAP (faster and richer than raw WHOIS)
                          and following redirect chains for URL shorteners
                          and cloud-storage redirect cloakers
    LWP::ConnCache      -- enables HTTP connection reuse (used with LWP::UserAgent)
    HTML::LinkExtor     -- enables structural HTML link extraction with entity decoding
    CHI                 -- enables cross-message IP/domain result caching
    IO::Socket::IP      -- enables IPv6 WHOIS connections
    Domain::PublicSuffix -- enables accurate eTLD+1 domain normalisation
    AnyEvent::DNS       -- enables parallel DNS resolution for multiple URL hosts

# LIMITATIONS

- No charset conversion

    Body text is stored as raw bytes.  Non-ASCII content (UTF-8, Latin-1,
    ISO-2022-JP, etc.) is not decoded to Perl's internal Unicode representation.
    URL and domain extraction from non-ASCII bodies may miss or misparse content.
    Use `Email::MIME` if full charset support is needed.

- Hand-rolled MIME parser

    The built-in MIME parser handles common cases but is not a conforming
    implementation of RFC 2045/2046.  It silently drops parts it cannot decode,
    does not handle `message/rfc822` attachments, and does not parse
    `Content-Disposition` filenames.  Replace with `Email::MIME` or
    `MIME::Entity` for production use with untrusted input.

- IPv4-only CIDR matching for trusted\_relays

    `_ip_in_cidr()` and the `trusted_relays` constructor argument only support
    IPv4 CIDR notation.  IPv6 trusted relay entries are accepted but silently
    never match.

- WHOIS rate-limiting not handled

    `_raw_whois()` does not retry on rate-limit responses (typically a
    "quota exceeded" reply).  Under high-volume processing the module will
    silently return empty enrichment data for affected IPs and domains.

- Not thread-safe

    The class-level `$_cache` variable and the optional-module `$HAS_*` flags
    are shared across all threads.  Create a separate object per thread and do
    not share objects across threads.

- DMARC policy not fetched

    The module reads the `Authentication-Results: dmarc=` result from the
    message headers but does not perform live `_dmarc.domain` TXT record
    lookups.  A missing DMARC result in the headers is not independently flagged.

- `abuse_contacts()` routes duplicated in `form_contacts()`

    Both methods iterate the same six discovery routes independently.  Any new
    discovery route must be added to both.  A future refactor should share a
    single routing pass.

- CHI cache is a class-level mutable global

    The cross-message cache is shared across all instances in the process.
    Tests that populate the cache will affect subsequent tests.  Pass the cache
    in via `new()` (not currently supported) to enable proper isolation.

# FORMAL SPECIFICATION

## new

    -- Z notation (simplified)
    new == [
      timeout        : N;
      trusted_relays : seq STRING;
      verbose        : BOOL;
      _raw           : STRING;
      _headers       : seq (STRING x STRING);
      _origin?       : IP_INFO | undefined;
      _urls?         : seq URL_INFO | undefined;



( run in 0.738 second using v1.01-cache-2.11-cpan-9169edd2b0e )