## Why It Matters

Your submission form is public, so it will be found by scanners, spray-and-pray bots, and the occasional person who thinks a broken link is a vulnerability. Kit filters that traffic before it reaches your triage board.

The cost of getting the filter wrong is asymmetric. A little spam in the queue is an annoyance. One silently blocked researcher is a real vulnerability you never hear about — and a person who tells other researchers your program is a black hole.

So the filters are deliberately narrow: they measure a **burst**, not a career. A researcher who files a hundred good reports over two years never trips them.

## The Five Gates

A submission passes through these in order. The first one that refuses it wins.

| Gate | Scope | Configurable | What refuses |
|------|-------|:------------:|--------------|
| Portal access | Whole portal | Yes | The portal is invite-only and the visitor has no invite |
| Bot checks | Per submission | — | A hidden honeypot field was filled, or the Cloudflare Turnstile challenge failed |
| Endpoint throttle | Per IP address | — | More than 10 submission attempts from one IP in an hour |
| Spam block | Per program, per email **and** per IP | **Yes** | The email or IP has an active block (see below) |
| Monthly report limit | Per program | — | The program has taken 25 reports this calendar month and has no VDP Add-on |

Only the spam block is yours to tune. The rest are fixed platform behaviour.

> [!NOTE]
> The endpoint throttle (10/hour per IP) and the spam block are different things and are often confused. The throttle counts *attempts* — including refused ones — and resets on its own. The spam block counts *accepted reports* and is the one that can leave a lasting record you can see and release.

## How Burst Blocks Work

Kit keeps a **spam record** for each identifier it has seen submit to your program — one keyed by email address, one keyed by IP address. Both are stored encrypted.

Each record counts submissions inside a **window**:

1. The first report opens a window and sets the counter to 1.
2. Every further report **inside that window** adds 1.
3. When the counter reaches **Max reports per window**, the record is blocked for the **Block Duration**, with the reason **Burst**.
4. A report that arrives **after the window has closed** opens a fresh window and resets the counter to 1 — and if the record was blocked, that block is retired at the same time.

The window is measured from the report that opened it, not from the last one. With the defaults — 5 reports per 300 seconds — that means five reports *within five minutes of the first*.

### A worked example

At the defaults (5 per 5 minutes, 1-hour block):

| Time | Event | Counter | Status |
|------|-------|:-------:|--------|
| 10:00 | Report 1 — opens the window | 1 | Watching |
| 10:01–10:03 | Reports 2, 3, 4 | 4 | Watching |
| 10:04 | Report 5 — **accepted**, and arms the block | 5 | Blocked (Burst) until 11:04 |
| 10:06 | Report 6 | 5 | Refused |
| 11:30 | Report 7 — block expired, window long gone | 1 | Watching |

Two things worth pinning down:

- **Max reports per window is the arming threshold, not the cap.** At 5, the fifth report is accepted and *then* the block engages. The sixth is the first one turned away.
- **A lapsed window retires the block.** The researcher does not have to wait out the block *and* be re-blocked on their next report. This is the bug this page's behaviour replaced: counting over a record's whole lifetime meant a researcher who had ever filed five reports — however many months apart — was blocked permanently, and every attempt to submit again re-armed the block.

> [!TIP]
> If you want to know whether a researcher is prolific or actually bursting, look at **Submissions** on the spam record: it is the count *in the current window*, not their lifetime total. Their lifetime work is on their researcher profile.

### Researchers you have already validated get more room

A researcher who has had at least one report on your program reach **Validated** or beyond, and whose karma is above zero, is measured against **four times** your threshold. At the defaults that is 20 reports in five minutes instead of 5.

The trust is earned on your program specifically — a researcher's standing with another team's program buys them nothing with yours — and it lifts the threshold on both their email record and the IP they submitted from, so they cannot pass the email gate only to be stopped by the other one.

It raises the bar; it never removes it. A trusted account that starts flooding — because it was compromised, or because the person behind it changed — is still blocked, just later. Karma falling back to zero (a dismissed-as-spam report costs 10 points) withdraws the trust immediately.

> [!IMPORTANT]
> If a burst block does land on a researcher you have already validated, every CSIRT admin is notified in-app, with a link straight to the record. That notification exists because the alternative is what it replaced: a researcher with fourteen reports — one of them a fixed, valid vulnerability — bouncing off an opaque error for a week while nothing anywhere said so. Treat it as a probable false positive until you have looked.

## Reading a Spam Record

**Audience: program owners and security team.** The console lives at [VDP → Spam Records](/csirt/spam_records) and requires the VDP Add-on.

Each row is one identifier — an email address or an IP. Click it to open the record and see why it is in the state it is in, along with the reports that identifier has already filed with you.

### Statuses

| Status | Meaning |
|--------|---------|
| **Watching** | Seen, counted, not blocked. The normal state for everyone who has ever submitted |
| **Blocked** | Submissions from this identifier are being refused right now |
| **Released** | Someone on your team lifted the block by hand |

### Why it was blocked

| Reason | Set by | Lifted by |
|--------|--------|-----------|
| **Burst** | Kit, automatically, when the counter hit your threshold | Waiting out **Blocked Until**, a lapsed window, or **Release** |
| **Manual** | A member of your team clicking **Block** | **Release** |

A manual block runs for **one week**, regardless of the Block Duration you configured — that setting governs automatic burst blocks only.

### The counters

| Field | What it tells you |
|-------|-------------------|
| **Submissions** | Reports accepted inside the current window |
| **Refused attempts** | How many times this identifier has been turned away while blocked |
| **Last seen** | The last accepted submission |
| **Blocked until** | When an automatic block lapses on its own. Empty means it does not |

**Refused attempts** is the number that tells you what kind of block you are looking at. A block with a handful of refused attempts and then silence is usually a researcher who gave up — worth checking. A block with hundreds is a bot, and the filter is doing its job.

## Blocking and Releasing by Hand

- **Block** puts a manual, one-week block on that identifier immediately.
- **Release** lifts a block of either kind and marks the record **Released**.

> [!IMPORTANT]
> Releasing is not an allowlist. A released identifier is watched again from scratch, and a fresh burst will block it again automatically. If a researcher legitimately needs to file in volume, raise your threshold — don't rely on repeatedly releasing them.

Blocks are per identifier, not per person. Someone blocked by email can still submit from the same IP under a different address, and releasing an email does nothing for a separately blocked IP. If a researcher reports being stuck after you released their email, check for an IP record too.

> [!WARNING]
> IP-keyed blocks catch bystanders. Corporate NAT, university networks, VPN exits, and Tor exit nodes put many unrelated people behind one address — and researchers use all four. Before blocking an IP by hand, check whether the reports behind it actually came from one person.

## Tuning the Thresholds

Configure these at [Program Settings → Spam](/csirt/program/spam_config/edit). See [Configuring Your Program](/docs/configuring-your-program#spam-tab) for the tab in context.

| Symptom | Change |
|---------|--------|
| Real researchers hit the block while working a target methodically | Raise **Max reports per window**, or shorten the **Window** so their reports fall into separate windows |
| Bots get through and fill the board | Lower **Max reports per window** and lengthen the **Block Duration** |
| One researcher files legitimate batches (e.g. a set of related findings written up separately) | Raise the threshold rather than releasing them repeatedly |
| Blocks are lingering longer than the abuse | Shorten the **Block Duration** — it only affects automatic blocks |

Guidance that holds at any setting: a burst threshold should be higher than the largest legitimate batch you would be happy to receive. Five reports in five minutes is fast for a human writing real reproduction steps, but it is not impossible — a researcher who prepared them offline and pasted them in will look exactly like a flood.

## What a Researcher Sees

**Audience: security researchers, and the staff who have to answer them.**

Kit never tells a refused submitter which filter caught them — a precise error message is a tuning oracle for whoever is abusing the form. The notices are correspondingly vague:

| Gate | Notice on the form |
|------|--------------------|
| Invite-only portal | "Invitation Required" — with a form to request access |
| Bot check | "Please complete the security verification and try again." |
| Endpoint throttle | "Too many submissions. Please try again later." |
| Spam block | "Your submission could not be processed at this time." |
| Monthly report limit | "This program has reached its monthly report limit. Please try again next month." |

If you are a researcher and your submission was refused:

1. **Wait an hour and try again.** Both the throttle and the default burst block clear on their own, and the burst block also clears once you stop submitting for the length of the program's window.
2. **Don't retry in a loop.** Every refused attempt is counted on the record. A wall of retries is what turns "probably a person" into "definitely a bot" for whoever reviews it.
3. **Send one plain email** to the contact address in the program's [security.txt](/docs/security-txt-setup) — usually `security@` — saying that the form refused you and roughly when. That address does not go through the submission filters.
4. **Include nothing sensitive in that email.** Ask them to unblock you; save the finding for the form.

If you run the program and a researcher tells you this happened, their identifier is in [Spam Records](/csirt/spam_records) — search for their email address, open the record, and release it.

## The Monthly Report Limit

Free programs accept **25 reports per calendar month**. The 26th is refused with the message above, and the counter resets at the start of the next month. The [VDP Add-on](/docs/vulnerability-disclosure-overview#whats-included) removes the limit.

This one is worth watching, because it is the only gate that gets *worse* the better your program does. A program that publishes a `security.txt` and starts getting real attention can pass 25 in a month without a single spam submission — and the reports it refuses are simply lost, not queued.

## Quick Checklist

- [ ] Confirm your burst threshold is higher than the largest legitimate batch you'd want to receive
- [ ] Skim [Spam Records](/csirt/spam_records) for **Blocked** rows with low **Refused attempts** — those are the ones that might be people
- [ ] Release anything that looks like a researcher, and raise the threshold rather than releasing them again next week
- [ ] Check for a separate IP record before telling a researcher they're unblocked
- [ ] If you're on the free tier and near 25 reports this month, decide before the 26th arrives

## Next Steps

- [Configuring Your Program](/docs/configuring-your-program#spam-tab) — the Spam tab alongside every other program setting
- [The Researcher Portal](/docs/the-researcher-portal) — the submission form, invite-only access, and magic-link portal
- [Triaging Reports](/docs/triaging-reports) — what to do with the submissions that get through
- [IP Investigation](/docs/ip-investigation) — profile an address before you block it by hand
- [Email Checker](/docs/email-checker) — tell a disposable inbox from a real one