Skip to content

fix: [ismalicious] count threat listings only, read categories per listing, add docs - #910

Merged
adulau merged 1 commit into
MISP:mainfrom
hexablob:ismalicious-threat-listings
Oct 5, 2026
Merged

adulau merged 1 commit into
MISP:mainfrom
hexablob:ismalicious-threat-listings

Conversation

@hexablob

@hexablob hexablob commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #798 (the isMalicious expansion/hover module). Disclosure: I maintain isMalicious.

What was wrong

  • Categories were never read. The /check response has no top-level categories for IPs, domains and URLs; each listing in sources carries its own category / categories (API reference). The summary therefore always fell back to classification.primary. Categories are now collected from the listings, with the same fallback.
  • Non-threat listings were counted as detections. Each listing carries a threatClass: threat (the default when absent), infrastructure (cloud and CDN ranges, Tor exits, DoH resolvers), policy (ads, tracking) or allowlist. Only threat listings mean the indicator was seen doing something malicious. A cloud egress IP listed only in a provider's published ranges used to come back as sources=1; it now reads sources=0, and those listings no longer contribute categories.

Also in this PR

  • Redirects are not followed (allow_redirects=False): requests would otherwise resend the X-API-KEY header to the new location. A 3xx (e.g. api_url set to http://) returns an explicit error.
  • Explicit error messages for 401 (key rejected) and 429 (rate limit or quota); other statuses unchanged.
  • User-Agent: ismalicious-misp/<version> instead of the generic misp-modules, so the API can tell this module's calls apart. Module version 1.0 → 1.1.
  • Documentation: the module was missing from README.md and the generated docs. Added the entry to README.md, documentation/mkdocs/index.md, and the blocks to documentation/README.md / documentation/mkdocs/expansion.md (produced with documentation/generate.py's own _generate_doc / _generate_index_doc for this module only, so the rest of the generated files is untouched), plus documentation/logos/ismalicious.png.
  • requirements metadata now says which value to paste: the X-API-KEY value (Base64 of apiKey:apiSecret) shown on the account page.

No new dependency. Input types, config keys (api_key, api_url) and output format (misp_standard, one text attribute) are unchanged.

Verification

  • pytest tests/test_ismalicious.py: 12 passed (HTTP mocked; new cases for per-listing categories, threat-only counting, the classification fallback, 401/429, redirect refusal, transport failure, User-Agent).
  • flake8 misp_modules/modules/expansion/ismalicious.py: clean. black --check and isort --check-only on both files: unchanged.

🤖 Generated with Claude Code

…sting, add docs

The /check response has no top-level `categories` for IPs, domains and
URLs: each listing in `sources` carries its own `category`/`categories`,
so the summary always fell back to `classification.primary`. Categories
are now collected from the listings.

Listings also carry a `threatClass`. Only `threat` (the default when
absent) says an indicator was seen doing something malicious;
`infrastructure` (cloud and CDN ranges, Tor exits, DoH resolvers),
`policy` and `allowlist` listings describe what it is. They no longer
feed the categories or the source count, so a cloud egress IP is not
reported as "sources=1".

Also:
- do not follow redirects (requests would resend X-API-KEY to the new
  location) and say so when api_url answers with one;
- explicit messages for 401 (rejected key) and 429 (rate limit/quota);
- User-Agent `ismalicious-misp/<version>`;
- module listed in README.md and the generated documentation, with logo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adulau
adulau merged commit 26b85b2 into MISP:main Oct 5, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants