← ~/blog
2026.08.07 · threat intel · Yash Nagare

ThreatPulse asks one question: is this scary for us?

Open a security news feed on any weekday morning and a couple hundred fresh stories are already waiting. A ransomware crew hit a hospital chain overnight. A popular open-source library turned out to be backdoored. Somebody's customer records are for sale again. And every single one of them arrives dressed as an emergency: critical, urgent, act now. That's the trap of security news. The volume is enormous, everything looks important, and when everything is critical, nothing is.

So the question a security leader actually carries into the office isn't "what happened in security today?" It's simpler, harder and a little selfish. Sure, it's important. But is it important to us?

ThreatPulse is a tool I built over five weeks to answer that question for one large enterprise. Every weekday morning it reads roughly 200 to 250 security news items from about 50 sources, decides which of them actually touch this specific organization, and posts a short briefing to Slack: an executive summary anyone can read in a minute, and a technical thread underneath for whoever needs the details. On a typical day, three to twelve items make it in. Everything else gets read, judged and set aside.

Here's roughly what lands in the channel, with every name and number invented:

Daily Threat Briefing · Tuesday · ELEVATED
reviewed 231 items from 52 sources · surfaced 6 · 2 touch our stack · 7 advisories deferred to patch management
An active credential-phishing campaign is impersonating Vantera-branded sign-in pages. Separately, the open-source package norvex-core, compromised upstream this week, is present on developer machines here; the name matches, the installed versions are unverified. No confirmed exploitation of our own stack today.
● FRAUD CAMPAIGN · phishing kit spoofing Vantera sign-in pages, unconfirmed but consistent with fraud we defend against · 2 sources
● SUPPLY CHAIN · norvex-core 2.3 to 2.6 backdoored upstream; name present on up to 2,800 developer machines here, versions unverified · 4 sources
● VENDOR INCIDENT · breach disclosed at Corvant Systems, which supplies software we run; no customer impact claimed yet · 3 sources
technical thread below: affected products, exploitation status, actor names, sources · ● high ● medium ● low
a mock briefing. every brand, package and number here is invented; only the shape is real

Most "threat intelligence" answers the wrong question

Inside most companies, the thing called threat intelligence is really a vulnerability feed wearing a costume. It's a list of CVEs (the catalog numbers assigned to software flaws) sorted by a severity score, and it leaves the reader to figure out which of several hundred entries actually matter. That's a useful function, but it's patch management. It answers "what is broken," not "who is coming for us."

ThreatPulse flips it. The briefing leads with what's emerging, the attacker side of the news: phishing and credential-theft campaigns running right now, the named groups behind them, the fraud patterns aimed at this kind of business specifically, breaches at similar companies, and break-ins at vendors whose software the organization runs.

A flaw in the company's own tech stack only makes the briefing when someone is actively exploiting it in the real world. Everything else gets dropped, and the briefing says how many it dropped and points to the patching team as the owner. That detail matters more than it looks. A tool that quietly ignores things looks broken. One that says "seven advisories deferred to patch management today" looks deliberate, and it keeps the peace with the team whose job that actually is.

It also makes the handoff useful in both directions. Vulnerability management answers "what is broken" better than any briefing ever will. What it drowns in is prioritization: a queue several hundred advisories deep with no way to know which one just caught fire. So when a stack flaw does clear ThreatPulse's bar, it arrives framed as an active threat with the evidence attached: not one more row in the queue, but this one, today. That split makes both sides better. The patching program gets a fire alarm instead of another list, and the intelligence side, freed from re-listing every CVE, only ever cries wolf about actual wolves. That's what keeps the briefing believable.

"Us" is the hard part

A burglary three states away is a news item. A burglary on your street is a warning. Same event, completely different meaning, and the difference is entirely a matter of where you live. For "is this scary for us" to mean anything, ThreatPulse has to know where the company lives.

So underneath everything sits a machine-readable picture of the company, and it has two halves. The first is written by a person: what industry the company is in, every business it owns, the brand names criminals impersonate in phishing, the business processes fraud actually goes after, and which vendors matter most. That half changes slowly, and someone who knows the business writes it better than any scanner could.

The second half is fetched automatically on every run: the asset inventories the company already keeps, listing the software it runs and the packages installed on its developer machines. Which tools supply that inventory will differ from company to company; the pipeline only needs the list to be fresh. Every story gets checked against both halves, and a story is relevant because it touches something in it, not because it's big security news. Show the exact same headline to a company that runs different software and holds different data, and it stays in the background.

This is the whole difference between news and intelligence. News tells you what happened. Intelligence tells you what it means for you, and there is no computing "for you" without a model of who you are.

Fourteen stages between the internet and your morning coffee

Here's what actually happens before that Slack message lands. The diagram is the map: fourteen stages, grouped into five boxes between the sources and Slack. Teal boxes are plain Python. The two purple boxes are the only places a language model (Claude) is involved. The tour below follows the boxes from top to bottom.

Vertical pipeline diagram: around 50 intelligence sources flow through five boxes (collect and normalize, cluster and enrich, classify and review, gate and suppress, write and fact-gate) out to a Slack briefing. Deterministic boxes are teal, the two boxes that involve a language model are purple, the organization's own relevance profile and fleet inventory feed in from the left, and an append-only audit trail runs alongside on the right.

Collect and normalize. Early in the morning, ThreatPulse wakes up and does the reading nobody wants to do. It pulls from around 50 sources: security vendor blogs, news outlets, research teams, government advisories, an industry sharing group and a feed that tracks compromised software packages. It asks each one the same question, what's new since yesterday?, so it never re-reads what it has already seen. Then it normalizes everything. Fifty sources means fifty formats, and every item becomes a title, a body, a timestamp and a source, no matter how it arrived.

Cluster and enrich. Six outlets, six different headlines, one actual event. Related articles get grouped into a single story that carries all its sources, and stories are remembered from one day to the next, so a campaign that develops over three days is one story with three days of history rather than three fresh alerts. This grouping quietly earns its keep twice. First, a reader sees each event once instead of six times, and that matters, because a briefing dies the day people start skimming it. Second, independent outlets reporting the same thing is what confirmation means, so the pile of sources under a story is also where the "how sure are we?" answer comes from.

Then comes enrichment. Every software flaw a story mentions gets checked against outside ground truth: its official CVE record, an exploit-prediction score (EPSS), and whether it sits in CISA's Known Exploited Vulnerabilities catalog, the U.S. government's list of flaws confirmed as exploited in the wild. "Actively exploited" in this system is a fact you can look up, not a vibe. Compromised package names get checked against what's actually installed on the organization's developer machines. Hold that thought; it's the best thing in the whole system.

Classify and review. Only now does a language model get its first turn: one structured judgment per story. Which channel does it belong to: fraud, supply chain, vendor incident, peer breach or a flaw in the company's own stack? How bad is it? How sure are we? What in the company's own profile does it touch? A second model then reviews everything the first one dismissed, and this reviewer has a deliberately strange job description: it is allowed to rescue stories, never to bury them. Think of an appeals court that can pardon but never convict.

Gate and suppress. The decision about what you actually see belongs to none of the models. It belongs to a bouncer, and the bouncer is code. Relevant fraud campaigns, vendor breaches and peer incidents walk straight in. Stack flaws get in only with evidence of active exploitation; the rest are counted and shown the door with a polite referral to patch management. And yesterday's story doesn't get to reintroduce itself unless it has something genuinely new to say.

Write and fact-gate. Then the prose gets written, mad-lib style (more on that in a minute), and a fact-checker with scissors goes through it: any sentence that can't point at its supporting evidence gets cut, and the cut is written down. Finally the briefing is assembled and posted, once. Run the whole machine twice by accident and you still get exactly one briefing, because it knows it has already spoken.

"Scary" is two questions, not one

Early on, the tool made a mistake that taught me something. A fraud campaign was rated high severity the day it broke. The next day, after two more outlets wrote up the same story, it came back rated medium. That's exactly backwards. Nothing about the threat had improved overnight. The story had been confirmed by more sources, and the tool treated confirmation as a reason to relax.

The mistake underneath is treating two questions as one: how bad is this, and how sure are we about it. A hurricane forecast keeps those dials separate for a reason. The storm's category and the forecasters' confidence in its track are different numbers, and a category five with an uncertain path is still a category five. More reports agreeing on the path don't weaken the storm. They firm up the warning.

Mix the two and you fail in both directions. A genuinely dangerous report from a single source gets quietly downgraded, so you're under-warned about the one thing that mattered. A trivial story covered by five outlets gets inflated, so you're alarmed about something that never deserved it.

ThreatPulse keeps them apart: how bad something is only changes when there's new information about the threat itself, while how sure we are moves freely as sources come in. And in the text a reader sees, uncertainty shows up as honest hedging about the sourcing, never as a reason to downplay. "Unconfirmed, but consistent with the fraud we already defend against" is the right sentence. "Unconfirmed, so probably ignore it" is not. The item made the briefing because it matters, and thin sourcing doesn't change that.

A two-by-two grid with severity on the vertical axis and confidence on the horizontal. High severity, low confidence: warn, and hedge. High severity, high confidence: lead the briefing. Low severity, low confidence: set aside. Low severity, high confidence: don't inflate. A story starts on day 1 at high severity and low confidence. After two more sources, it should move right into lead the briefing; the early bug instead moved it down into the low-severity row.

The scariest answer is "yes, and it's in the house"

The most useful thing ThreatPulse does is also the easiest to explain. When a supply-chain attack compromises a set of software packages, the tool takes those package names and checks them against a live inventory of what's actually installed on the organization's developer machines. "This package was backdoored" is news. "This package was backdoored, and it's sitting on machines here" is an exposure finding. It's the question in the title, answered at its most literal.

It has fired for real. During a real supply-chain worm outbreak, several of the compromised packages turned out to be present in the company's inventory. And one caveat travels with that finding every single time it appears: the inventory knows a package is present, not which version of it is installed. So the finding means "the name is here, on these machines," not "these machines are compromised." That difference is what separates a useful early warning from a false alarm.

What that does to the clock

Security teams are judged on two clocks: mean time to detect (MTTD), how long something is wrong before you know, and mean time to respond (MTTR), how long you know before you act. Emerging threats are exactly where those clocks hurt, because every day of lag is a day of exposure.

Five weeks of runs can't produce an honest before-and-after chart, and I'd side-eye anyone who claimed otherwise. But the design puts a hard ceiling on detection lag: anything the sources report is in the next morning's briefing, already checked against what the company runs and what's installed on its machines. And whoever picks up an item starts from the evidence in the thread (affected products, exploitation status, indicators) instead of a blank search box.

Keeping the AI part honest

You may have noticed the model only gets a say in four of those fourteen stages: the classifier, the critic, the writer and the fact-checker, all inside the two purple boxes. That ratio is the point. For a briefing, a made-up number or a made-up identifier is worse than silence, because one invented detail poisons the reader's trust in everything else on the page. So every decision about which stories reach a reader is made by ordinary, checkable code, and the model works inside fences.

The writing step is essentially a mad-lib. The model gets each story with every number, date and identifier replaced by a blank, writes its sentences around the blanks, and ordinary code fills in the true values at the end from the stored facts. It cannot invent a number because it never sees one. The blanks weren't the first plan. The first plan was asking nicely, and I learned the hard way that telling a model not to do something it can plainly see how to do does not work. It kept copying the real digits into its prose anyway, politely, twice. What worked was removing the temptation entirely.

Where it goes next: from warning to hunt

The technical thread under every briefing already lists the fingerprints an attack leaves behind: domains, IP addresses, malware file hashes. Security people call them indicators of compromise (IOCs). Today they're printed "defanged" (evil[.]com instead of a clickable evil.com) and a defender chases them by hand. The next phase sweeps every indicator through whatever log platform is already watching (Splunk, an ELK stack) and asks one question: have we seen this before?

That's the last rung of the ladder the whole tool climbs. Is this campaign real? Does it touch what we run? Is the poisoned package in our house? Has any of it already been here? A log hit turns a briefing item into an incident with a timestamp attached. And it keeps the same discipline as the inventory check: a clean sweep means the logs don't show it, not that it never happened.

Where it fell short

The best real finding it ever produced never reached a reader through the tool. The supply-chain match fired a day before the briefing had a section to display that kind of finding, and by the following morning the story was old news to the system, so it stayed quiet, exactly as designed. The finding got to the security team through a plain human message instead. Missed by one day, and I'd rather say so than pretend otherwise.

It also exposed a flaw in the bouncer. "Yesterday's story doesn't get to reintroduce itself" is a good rule until the new information is that the threat is on your own machines. So I reworked the prompt and tightened the suppression process: a fresh hit against the company's own inventory now counts as something genuinely new to say, even when the headline is a day old.

That's really the whole idea. The point was never to build something that knows everything about security. It was to build something that answers one question every morning and is worth believing when it says yes. Is this scary for us?