---
name: automateyournetwork/cisco-psirt-advisories
source: https://app.decimal.ai/s/automateyournetwork-cisco-psirt-advisories@1/SKILL.md
source_sha256: 6f929fa67ffe
---

# Cisco PSIRT Advisory Checks

Answers one question well: **is the software this device is running affected by a Cisco
security advisory?**

## Read this first — an empty result is not a clean bill of health

Every tool returns one of five `outcome` values. Two of them look similar in the data
and mean completely different things:

| `outcome` | What it means |
|---|---|
| `advisories_found` | Cisco has published advisories for this version |
| `none_published` | Cisco has published **nothing** for this version — **NOT "the device is secure"** |
| `normalisation_failed` | The version could not be parsed. **Nothing was checked.** |
| `unsupported_ostype` | Not a PSIRT OS family (includes `iosxr` and every non-Cisco platform) |
| `api_error` | Auth failure, rate limit, or a version Cisco has no record of |

**Never report "no advisories" as "not vulnerable".** `none_published` means Cisco has
published nothing matching that exact version string. It says nothing about
unpatched-but-unpublished issues, configuration weaknesses, or anything outside Cisco's
PSIRT process.

**Never treat `normalisation_failed` or `api_error` as good news.** Both mean the question
went unasked. In a fleet sweep, check the `outcome_summary` counts before telling anyone
the fleet is clean — devices in those two buckets were never checked at all.

## The two-step chain

This server **never contacts a device**. It has no transport, no credentials for your
network, and no way to read a version. You supply the version:

**Step 1 — read the version off the device.**

```bash
# Cisco platforms: pyATS
python3 $MCP_CALL "python3 -u $PYATS_MCP_SCRIPT" run_show_command \
  '{"device_name":"cat9k-1","command":"show version"}'

# Anything else, or when pyATS has no parser: the multivendor CLI driver
python3 $MCP_CALL "python3 -u $MULTIVENDOR_MCP_SCRIPT" device_run_command \
  '{"device":"rtr-1","command":"show version"}'
```

**Step 2 — pass it here.** Full `show version` output is fine; the banner is parsed.

```bash
python3 $MCP_CALL "python3 -u $CISCO_PSIRT_MCP_SCRIPT" check_version \
  '{"ostype":"iosxe","version":"17.3.1"}'
```

Do not skip step 1 and guess a version. The server refuses to infer one — a guessed
version returns advisories for software the device is not running, which is worse than
no answer.

## Available Tools

### 1. `check_version` — is this version affected?

```bash
python3 $MCP_CALL "python3 -u $CISCO_PSIRT_MCP_SCRIPT" check_version \
  '{"ostype":"iosxe","version":"17.3.1"}'
```

- `ostype` (required): `ios` | `iosxe` | `nxos` | `asa` | `fmc` | `ftd` | `aci`
- `version` (required): bare version or full `show version` output
- `refresh` (optional, default `false`): bypass the 6-hour cache

Verified: `iosxe` + `17.3.1` returns **122 advisories**.

### 2. `check_versions` — a fleet in one call

```bash
python3 $MCP_CALL "python3 -u $CISCO_PSIRT_MCP_SCRIPT" check_versions \
  '{"devices":[{"name":"cat9k-1","ostype":"iosxe","version":"17.3.1"},
               {"name":"n9k-1","ostype":"nxos","version":"9.3(5)"}]}'
```

**Prefer this over looping `check_version`.** It de-duplicates by version first, so 60
devices running 12 distinct versions cost 12 API calls rather than 60 — the difference
between one-third of the per-minute budget and twice it. One device failing never aborts
the others.

### 3. `check_cve` — which Cisco advisories cover this CVE?

```bash
python3 $MCP_CALL "python3 -u $CISCO_PSIRT_MCP_SCRIPT" check_cve '{"cve":"CVE-2024-20353"}'
```

### 4. `check_advisory` — one advisory by id

```bash
python3 $MCP_CALL "python3 -u $CISCO_PSIRT_MCP_SCRIPT" check_advisory \
  '{"advisory_id":"cisco-sa-bootp-WuBhNBxA"}'
```

### 5. `list_recent` — what has Cisco published lately?

```bash
python3 $MCP_CALL "python3 -u $CISCO_PSIRT_MCP_SCRIPT" list_recent \
  '{"severity":"critical","start_date":"2026-01-01","end_date":"2026-07-31"}'
```

Verified: `critical` across 2026 to date returns **15 advisories**.

### 6. `psirt_status` — check the budget before a sweep

```bash
python3 $MCP_CALL "python3 -u $CISCO_PSIRT_MCP_SCRIPT" psirt_status '{}'
```

Reports auth state, remaining rate budget, cache statistics, and which OS families are
supported. Contains no credential values. Worth calling before a large sweep.

## Version formats differ per family — and they contradict each other

This is the most common source of a wrong answer. The server normalises for you, but you
must collect the **right number** in the first place.

| OSType | Format Cisco expects | Example |
|---|---|---|
| `iosxe` | dotted | `17.3.1`, `17.03.01`, `17.3.1a` |
| `ios` | parenthesised, letter outside | `15.2(4)E`, `15.2(4)E10` |
| `nxos` | parenthesised | `9.3(5)` |
| `asa` | dotted | `9.16.1` |
| `ftd` / `fmc` | dotted | `7.0.1` |
| `aci` | parenthesised, letter **inside** | `15.2(3e)`, `16.0(3e)` |

Note the contradiction: `iosxe` **rejects** `17.3(1)` while `ios` **rejects** `15.2.4E`.
The same-looking transformation runs in opposite directions depending on family. The
server handles the conversion; pass whatever the device reported.

**ACI is the trap.** It wants the **switch image version** (`15.2(3e)`), not the APIC
controller version — `5.2(3e)` is rejected outright. Collect it from a switch, not the
APIC.

## IOS-XR is not supported, and this will surprise you

`iosxr` is **not an OSType on this API**. Every version tried (7.5.2, 6.6.3, 24.1.1)
returns HTTP 404, against an `iosxe` 200 control in the same session.

This is worth flagging to the user rather than working around silently, because NetClaw
*can* reach IOS-XR devices through pyATS — so an operator will reasonably expect the
version check to work. For IOS-XR, fall back to `check_cve` or advisory lookup by
product id, and say plainly that per-version checking is unavailable for that platform.

## Where this ends and `nvd-cve` begins

Both answer vulnerability questions; they are not interchangeable, and neither is a
substitute for the other.

| Question | Use |
|---|---|
| "Is this Cisco version affected by a Cisco advisory?" | **this skill** |
| "What is CVE-2024-20353, what is its CVSS, what is affected?" | `nvd-cve` |
| "Has Cisco issued an advisory for this CVE?" | **this skill** (`check_cve`) |
| "Does any vendor have a known CVE matching this software?" | `nvd-cve` |

**Either can legitimately be empty while the other is not.** Cisco may publish an
advisory before an NVD entry exists; NVD may hold a CVE for which Cisco has issued no
advisory. When a security question matters, check both and say which one answered.

## Rate limits are shared and tight

5 calls/second and **30 calls/minute**, shared across every caller of the credential.
30/minute is the real constraint. The server handles this automatically — de-duplicating
by version, serving from a 6-hour cache, pacing, and backing off on 429 — but two habits
defeat it:

- **Looping `check_version` per device** instead of using `check_versions`.
- **Passing `refresh: true` routinely.** It disables the cache. Use it during an incident
  when cache age is itself the question, not as a default.

## What this API does not provide

Measured, not inferred from documentation — do not spend time re-testing these:

- **Bug, EoX, Case and Serial-to-Info APIs** return **403** under the API Console grant.
- **CX Cloud** returns **504** on all seven paths tried. It needs a separate tenant
  subscription.
- **IOS-XR** returns **404**, as above.

## Reporting results to a user

Lead with the count and the worst severity, not the whole list. Then, if the answer was
`none_published`, say what that does and does not mean — the distinction is the point of
this skill, and it is the part a reader will otherwise get wrong.

Good: *"cat9k-1 on IOS-XE 17.3.1 has 122 published advisories, 4 of them Critical
(highest CVSS 9.8). Recommend reviewing the Critical set first."*

Good: *"n9k-1 on NX-OS 9.3(5) — Cisco has published no advisory matching this exact
version. That is not confirmation the device is secure; it means nothing is published for
this version string."*

Bad: *"n9k-1 is not vulnerable."* Never say this.