feat: Zeek Intel export format + public campaigns API endpoint - #15
Merged
Conversation
Adds `zeek` as a sixth indicator export format alongside CSV, STIX 2.1, MISP JSON, and Snort/Suricata — the defensive counterpart to the Snort exporter, loadable directly by Zeek's `Intel::read_files`. Details ------- * `toZeekIntel()` emits a `#fields` header followed by tab-separated rows. `zeekIntelType()` maps indicator types to `Intel::Type`: IPs → ADDR, domains → DOMAIN, URLs → URL, emails → EMAIL, MD5/SHA1/SHA256 all fold into FILE_HASH, filenames → FILE_NAME. Types Zeek cannot match on (CVE, ASN, registry key, mutex, BTC address, user agent) are omitted, the same rule the Snort exporter follows. * URL indicators have their scheme stripped (Zeek matches host+URI); domains and hashes use the normalized (lowercased, refanged) form. * Unlike the Snort exporter, skipped indicators are NOT annotated in a trailing comment: Zeek's ASCII input reader parses `#` lines after the header as data, so a comment there would become a bogus indicator. Keeping the body strictly header+rows is what makes the file loadable. * Any indicator whose value contains a tab or newline (only possible from a malformed feed) is dropped rather than shift the tab-delimited columns. * Wired end to end automatically: the export route validates against `EXPORT_FORMATS` and the indicators export menu renders from it, so the new format needs no route or UI change. Docs kept in sync: README endpoint list, docs/API.md format table, and the OpenAPI `format` enum all gain `zeek`. Tests: 8 new cases in formats.test.ts (type mapping incl. hash folding, header + row shape, URL scheme stripping, non-matchable omission with no stray `#` line, tab-injection drop, meta columns, default source) plus the dispatch test. Full suite: 26 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the campaigns collection to the read-only public API, the programmatic equivalent of the /campaigns UI. Mirrors the existing /api/v1/actors route: bearer-key auth, per-key rate limiting, and cursor pagination. Details ------- * New `campaigns:read` scope in `API_SCOPES`. Because the Settings UI renders its checkbox list from that same array, the scope appears for key creation automatically — one list, never two to keep in sync. * `GET /api/v1/campaigns` supports `page`, `pageSize` (default 50, max 200), and a `status` filter validated by the existing `parseCampaignStatus` helper (SUSPECTED / ACTIVE / DORMANT / CONCLUDED). * Each row carries `actorCount`, `techniqueCount`, and `indicatorCount` (via a single Prisma `_count`) so a consumer can gauge a campaign's scope without a follow-up request. Ordered by start date, nulls last. Docs kept in sync: README endpoint list, docs/API.md scope table + a new Campaigns section, and the OpenAPI spec (new `Campaign` schema, `CampaignPageSize` parameter, and `/api/v1/campaigns` path). Verified: tsc --noEmit clean, eslint clean (no new warnings), full vitest suite 188 passed, and the OpenAPI document parses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two self-contained, tested additions to the CTI surface. Both verified against the repo's gates:
tsc --noEmitclean,eslintclean (no new warnings), fullvitestsuite 188 passed.1. Zeek Intel framework export (
feat(export))Adds
zeekas a sixth indicator export format — the defensive counterpart to the existing Snort/Suricata exporter, loadable directly by Zeek'sIntel::read_files.toZeekIntel()emits a#fieldsheader + tab-separated rows.zeekIntelType()maps types toIntel::Type(IP→ADDR, domain→DOMAIN, URL→URL, email→EMAIL, MD5/SHA1/SHA256→FILE_HASH, filename→FILE_NAME); unmatchable types (CVE, ASN, registry key, mutex, BTC, user-agent) are omitted, same rule Snort follows.#comment for skipped items — Zeek's ASCII reader parses#lines after the header as data, so a comment would become a bogus indicator. Body stays strictly header+rows so the file loads.EXPORT_FORMATSand the menu renders from it.2. Public
GET /api/v1/campaigns(feat(api))The programmatic equivalent of
/campaigns, mirroring the/api/v1/actorsroute (bearer-key auth, per-key rate limiting, pagination).campaigns:readscope — auto-appears in the Settings key-creation UI, which renders from the sameAPI_SCOPESarray.page,pageSize(default 50, max 200), and astatusfilter validated by the existingparseCampaignStatushelper.actorCount/techniqueCount/indicatorCountvia one Prisma_count, so consumers gauge scope without a second call.Docs kept in sync
README endpoint list,
docs/API.md(format table, scope table, new Campaigns section), anddocs/openapi.yaml(zeekin the format enum, newCampaignschema +/api/v1/campaignspath) — OpenAPI document parses.🤖 Generated with Claude Code