Free feed · JSON and badge

Cyber Weather in your SOC dashboard

Today’s global threat condition, the flaws CISA confirmed as exploited this week and three things to do, as one JSON document your SIEM or wallboard can read. No key, no sign-up.

The feed

One GET returns the day’s reading: https://www.bitscore.in/api/connector/cyber_weather_today. The answer has two parts: text, a summary to quote, and data, the same facts as fields. The same data is the cyber_weather_today tool on the MCP connector, and the OpenAPI description covers both.

  • The condition. condition (clear, cloudy, showers, storm, severe), points of max_points, and parts: each of the five instruments with its points and what it read.
  • The dates. issued_for is the IST day of the forecast, data_day the UTC day the figures describe, fetched_at when the feeds were read.
  • The detail. kev_7d (CISA’s exploited flaws this week, with ransomware use and due dates), epss_risers and watch_list, outlook, traffic, patch_calendar, outages and actions.
  • Where it came from. sources lists every source with its licence, and field_sources says which source each field is drawn from.

The schema is versioned (schema_version, now 1.0). New fields may be added within a version; a field is renamed or removed only with a new major version.

When to poll

  • The reading is rebuilt daily at 07:30 IST (02:00 UTC), with a catch-up at 08:30 IST if a feed failed. Poll once a day, after 08:45 IST (03:15 UTC).
  • Responses are cached until shortly after the next refresh, so polling more often returns the same reading. Requests are rate-limited per IP address.
  • If a refresh fails, the previous reading stays up with its own date. stale turns true once it is more than 26 hours old; alert on that rather than on the HTTP status.
  • There is no SLA. It is a free, best-effort feed from public sources, and a source can be late or change its format.

curl

curl -s https://www.bitscore.in/api/connector/cyber_weather_today \
  | jq '.data | {issued_for, condition_label, points, max_points, stale}'

Splunk

A scripted input in its own app. Make the script executable, and create the index first or change it.

bin/cyber_weather.sh
#!/bin/sh
curl -sf --max-time 30 https://www.bitscore.in/api/connector/cyber_weather_today
echo
local/inputs.conf
[script://$SPLUNK_HOME/etc/apps/bitscore_cyber_weather/bin/cyber_weather.sh]
# 08:45 IST is 03:15 UTC. The schedule runs in the Splunk server's time zone.
interval = 15 3 * * *
index = threat_intel
sourcetype = bitscore:cyber_weather
disabled = 0
local/props.conf
[bitscore:cyber_weather]
KV_MODE = json
SHOULD_LINEMERGE = false
TRUNCATE = 0
DATETIME_CONFIG = CURRENT

TRUNCATE = 0 matters: a busy week’s reading runs past Splunk’s default 10,000-byte event limit. Then:

Search
index=threat_intel sourcetype=bitscore:cyber_weather
| head 1
| table data.issued_for data.condition_label data.points data.max_points data.stale

Microsoft Sentinel

A Logic App that reads the feed each morning and sends one row to a custom table through the Logs Ingestion API.

  • In the Sentinel workspace, create a DCR-based custom table named CyberWeather (Tables → Create → New custom log). Use a data collection endpoint, and give it the columns TimeGenerated (datetime), IssuedFor and Condition (string), Points (int), Stale (boolean) and Feed (dynamic).
  • Create a Logic App with a system-assigned managed identity, and give that identity the Monitoring Metrics Publisher role on the data collection rule.
  • Paste the workflow below into the code view, with your endpoint and the rule’s immutable ID.
Logic App code view
{
  "definition": {
    "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
    "contentVersion": "1.0.0.0",
    "triggers": {
      "Daily_0845_IST": {
        "type": "Recurrence",
        "recurrence": {
          "frequency": "Day",
          "interval": 1,
          "timeZone": "India Standard Time",
          "schedule": { "hours": ["8"], "minutes": [45] }
        }
      }
    },
    "actions": {
      "Get_Cyber_Weather": {
        "type": "Http",
        "runAfter": {},
        "inputs": { "method": "GET", "uri": "https://www.bitscore.in/api/connector/cyber_weather_today" }
      },
      "Send_to_Log_Analytics": {
        "type": "Http",
        "runAfter": { "Get_Cyber_Weather": ["Succeeded"] },
        "inputs": {
          "method": "POST",
          "uri": "https://<your-dce>.ingest.monitor.azure.com/dataCollectionRules/<dcr-immutable-id>/streams/Custom-CyberWeather_CL?api-version=2023-01-01",
          "headers": { "Content-Type": "application/json" },
          "body": [
            {
              "TimeGenerated": "@{utcNow()}",
              "IssuedFor": "@{body('Get_Cyber_Weather')?['data']?['issued_for']}",
              "Condition": "@{body('Get_Cyber_Weather')?['data']?['condition']}",
              "Points": "@body('Get_Cyber_Weather')?['data']?['points']",
              "Stale": "@body('Get_Cyber_Weather')?['data']?['stale']",
              "Feed": "@body('Get_Cyber_Weather')?['data']"
            }
          ],
          "authentication": { "type": "ManagedServiceIdentity", "audience": "https://monitor.azure.com" }
        }
      }
    },
    "outputs": {}
  }
}
KQL
CyberWeather_CL
| summarize arg_max(TimeGenerated, *) by IssuedFor
| project IssuedFor, Condition, Points, Stale
| order by IssuedFor desc

Elastic

Filebeat’s HTTP JSON input. Hourly is fine, because the response is cached; the fingerprint gives each reading one document, so repeat polls overwrite rather than duplicate.

filebeat.yml
filebeat.inputs:
  - type: httpjson
    id: bitscore-cyber-weather
    interval: 1h
    request.url: https://www.bitscore.in/api/connector/cyber_weather_today
    request.method: GET
    processors:
      - decode_json_fields:
          fields: ["message"]
          target: "bitscore"
      - fingerprint:
          fields: ["bitscore.data.issued_for", "bitscore.data.fetched_at"]
          target_field: "@metadata._id"
      - drop_fields:
          fields: ["message", "bitscore.text"]

Grafana

The Infinity data source plugin. Provision it with the site allowed, then add a table panel with the query below. For a stat panel of the condition, set the root selector to data and pick condition_label, points and stale.

provisioning/datasources/bitscore-cyber-weather.yaml
apiVersion: 1
datasources:
  - name: BitScore Cyber Weather
    type: yesoreyeram-infinity-datasource
    jsonData:
      allowedHosts:
        - https://www.bitscore.in
Panel query (Infinity)
{
  "refId": "A",
  "type": "json",
  "source": "url",
  "parser": "backend",
  "format": "table",
  "url": "https://www.bitscore.in/api/connector/cyber_weather_today",
  "url_options": { "method": "GET" },
  "root_selector": "data.parts",
  "columns": [
    { "selector": "label", "text": "Instrument", "type": "string" },
    { "selector": "points", "text": "Points", "type": "number" },
    { "selector": "max_points", "text": "Out of", "type": "number" }
  ]
}

Badge for wallboards and intranets

Today’s condition as an image, updated with each refresh.

Today’s Cyber Weather badge
HTML
<a href="https://www.bitscore.in/cyber-weather?utm_source=badge&utm_medium=embed&utm_campaign=cyber-weather">
  <img src="https://www.bitscore.in/cyber-weather/badge.svg" width="280" height="64"
       alt="Today’s Cyber Weather by BitScore">
</a>

Credit and licences

Show Cyber Weather by BitScore with a link to the forecast page wherever the data appears. Each source keeps its own terms, listed in the feed’s sources:

  • Non-commercial. Fields from SANS ISC (traffic, ports, Infocon, and the wind and pressure instruments) are CC BY-NC-SA 4.0; Cloudflare Radar’s outages and DDoS trend are CC BY-NC 4.0. Inside a commercial product or service, leave those fields out or ask the source.
  • Open. CISA’s KEV is public domain, abuse.ch data is CC0, EPSS is free with credit to FIRST, and CVE records follow the CVE Program’s terms of use.

What the feed is not

Indicative only, and not a measure of any organisation’s risk. Cyber Weather reports aggregate, public signals and names no affected organisation.

The feed covers everyone. Your rating covers you.

The free domain check reads what attackers can see of your organisation from outside. The rating report goes further, with an independent 250–900 security rating.

Check my domain →Get your free rating