Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ venv/
# Inputs / outputs
inventory.xlsx
analysis_output_*.xlsx
impact_report_*.html
~$*.xlsx

# Inventory / output folders (contents are local data, not source)
Expand Down
2 changes: 1 addition & 1 deletion .specify/feature.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"feature_directory": "specs/005-self-uninstall"
"feature_directory": "specs/006-api-inventory-check"
}
81 changes: 58 additions & 23 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
<!--
Sync Impact Report
==================
Version change: 2.0.0 → 2.1.0
Rationale: Relaxed Principle II so that installing as a `uv` tool is the *primary, recommended*
distribution rather than the *sole* one: a `pip install .` from a source checkout
(into a virtual environment) is now a supported fallback for environments where `uv`
cannot be installed. This adds an allowance without removing or redefining a principle
(no bespoke installers return), so the bump is MINOR. The Technology & Data Constraints
Distribution bullet was updated to match. README and docs/index.html document the
no-uv path. This constitution's own version is independent of the product version.
Version change: 2.1.0 → 2.2.0
Rationale: Enacted as a prerequisite of spec 006 (inventory-driven vulnerability check via the
Cisco PSIRT API). Four changes, none of which removes or redefines a principle, so the
bump is MINOR. (1) Principle IV now states the *substance* of the undecided result —
it must be labelled undecided and never guessed — while allowing each mode the clearest
label for its own report; `Indeterminate` (advisory-driven) and `Needs review`
(inventory-driven) are both conforming. The same principle now also makes explicit what
was previously only implied: a "not affected" determination MUST rest on positive
confirmation, not on the absence of a result. (2) Source of truth admits Cisco's PSIRT
openVuln advisory API alongside ERP pages and CSAF JSON. (3) Networking admits
`apix.cisco.com` and `id.cisco.com`, scoped, with an explicit note on what is sent.
(4) Inputs/outputs records the `impact_report_*.html` report the new mode writes.
This constitution's own version is independent of the product version.

Modified principles:
- II. Cross-Platform Parity (uv is now primary/recommended; pip-from-source added as a supported fallback)
- IV. Conservative & Traceable Analysis (undecided label is mode-appropriate but mandatory;
"not affected" now explicitly requires positive confirmation; PSIRT API added as traceable
source data; AI-normalization clause scoped to modes that use AI)
Added sections: (none)
Modified sections:
- Technology & Data Constraints (Distribution bullet notes the pip-from-source fallback)
- Technology & Data Constraints (Source of truth, Networking, Inputs/outputs bullets)
Removed sections: (none)

Templates requiring updates:
- .specify/templates/plan-template.md ✅ aligned (Constitution Check resolves gates dynamically)
- .specify/templates/spec-template.md ✅ aligned (no constitution references)
- .specify/templates/tasks-template.md ✅ aligned (no constitution references)

Follow-up TODOs: (none)
Follow-up TODOs: README.md and docs/index.html must document the new mode, its Cisco credential
prerequisite, and its HTML output before spec 006 merges (Documentation quality gate).

Previous version 2.1.0 (2026-07-18): relaxed Principle II so a `uv` tool install is the primary,
recommended distribution rather than the sole one, adding `pip install .` from a source checkout
as a supported fallback; Technology & Data Constraints Distribution bullet updated to match. MINOR.
Previous version 2.0.0 (2026-07-18): redefined Principle II for uv-tool distribution (dropped the
install.py/run.py/.venv mandate); updated Principle V (per-user config secrets) and Technology &
Data Constraints (uv-based updates, working-folder I/O). Enacted spec 004 (FR-024). MAJOR.
Expand Down Expand Up @@ -83,14 +94,26 @@ the tool debuggable and honest about what it did.
### IV. Conservative & Traceable Analysis (NON-NEGOTIABLE)
Impact determinations MUST NOT overstate risk. When the available data is insufficient
to decide — for example when impact depends on runtime configuration the inventory does
not track — the result MUST be `Indeterminate`, never a guess. Every determination MUST
be traceable to official Cisco source data (the ERP page and each advisory's CSAF JSON);
the tool MUST NOT invent advisory facts. AI-derived output MUST be normalized and
validated before it reaches the report.
not track, or when the authoritative source does not recognize a device's software
release or hardware platform — the result MUST be labelled as undecided, never guessed.
The label MUST be unambiguous within the report it appears in; `Indeterminate` in the
advisory-driven analysis and `Needs review` in the inventory-driven check are both
conforming. A determination that a device is *not* affected MUST rest on positive
confirmation from official Cisco source data, never on the mere absence of a result; an
undecided device MUST NOT be presented as unaffected. Every determination MUST be
traceable to official Cisco source data (an ERP page, an advisory's CSAF JSON, or Cisco's
PSIRT advisory API); the tool MUST NOT invent advisory facts. Where a mode uses AI to
reach a determination, that output MUST be normalized and validated before it reaches the
report.

Rationale: People act on these reports to protect firewalls. A false "Not Affected" is
dangerous and a fabricated "Affected" erodes trust; conservative, source-anchored
results are the only acceptable posture.
results are the only acceptable posture. Each mode may choose the clearest label for its
own report, but the substance is fixed: an undecidable device is never presented as safe,
and safety is asserted only when the source data affirms it. The positive-confirmation
rule exists because an authoritative source can return "nothing found" both for a device
that is genuinely clean and for one it cannot recognize at all — treating those alike
would silently convert a data-quality problem into a clean bill of health.

### V. Secrets Hygiene & Data Locality
API keys and other secrets MUST live only in the per-user configuration file (or in
Expand All @@ -115,16 +138,28 @@ a personal credential; leaking either is a direct harm.
`--update` is uv-specific and does not apply on that path. The product version is single-sourced
from the committed `VERSION` file into the package metadata at build time.
- Source of truth: Cisco Event Response (ERP) pages and official advisory CSAF JSON
fetched from `sec.cloudapps.cisco.com`. No scraped or cached-as-authoritative
alternatives.
fetched from `sec.cloudapps.cisco.com`, and Cisco's PSIRT openVuln advisory API — the
same published data that backs Cisco's public Software Checker — for release- and
platform-scoped advisory lookups and for the platform/release catalogues those lookups
are validated against. Catalogues MUST be retrieved from Cisco rather than hardcoded, so
newly published releases and platforms are picked up without a tool release. No scraped
or cached-as-authoritative alternatives.
- AI provider: FueliX (OpenAI-compatible Chat Completions endpoint) accessed over the
standard library, using the current default model configured in `fuelix.py`.
- Inputs/outputs: the inventory is a user-provided `.xlsx` (sheet `FW_List`) located in the
current working folder; reports are written as timestamped `analysis_output_*.xlsx` files
into an `output/` folder inside that same working folder. Header matching MUST stay
tolerant of minor naming variation.
current working folder; reports are written into an `output/` folder inside that same
working folder as timestamped files — `analysis_output_*.xlsx` for the advisory-driven
analysis and `impact_report_*.html` for the inventory-driven check. An HTML report MUST be
a single self-contained file that renders with no network access, so it survives being
emailed or archived. Reports MUST never be overwritten. Header matching MUST stay tolerant
of minor naming variation.
- Networking: the analyzer itself contacts only `sec.cloudapps.cisco.com` (Cisco
ERP/CSAF) and `api.fuelix.ai` (AI). Version and update checks additionally contact
ERP/CSAF), `api.fuelix.ai` (AI), `apix.cisco.com` (Cisco PSIRT advisory data and
platform/release catalogues) and `id.cisco.com` (short-lived access token for that API).
To the Cisco advisory API the tool MUST send only the derived product family, hardware
platform and software release being checked — never a firewall name or any other inventory
identifier — keeping that disclosure strictly smaller than what the AI endpoint receives.
Version and update checks additionally contact
GitHub — `api.github.com` and `github.com` — solely to discover the latest published
release; they send no inventory or secrets and MUST be best-effort and time-bounded so
they never block a run. Updating is delegated to `uv`, which reinstalls the tool from the
Expand Down Expand Up @@ -190,4 +225,4 @@ Compliance is verified at review time. Dependent Spec Kit artifacts (plan, spec,
tasks templates and their generated documents) MUST remain consistent with the principles
above; the Constitution Check in the planning workflow enforces this on each feature.

**Version**: 2.1.0 | **Ratified**: 2026-07-14 | **Last Amended**: 2026-07-18
**Version**: 2.2.0 | **Ratified**: 2026-07-14 | **Last Amended**: 2026-07-25
105 changes: 95 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ Windows, and Linux**.
| - | ----------- | ------------- |
| 1 | **uv** (recommended) — or just **Python 3.9+** | It manages Python for you. Not sure if you have it? See [Get uv](#get-uv) below — check first, install only if needed. Can't install uv? See [Install without uv](#install-without-uv-python-only) — you only need Python. |
| 2 | **Git** | The uv install command below pulls the tool straight from GitHub, so Git must be installed first. Not sure if you have it? See [Get Git](#get-git) below. |
| 3 | **A FueliX API key** | Get yours from <https://dev.fuelix.ai> — each person needs their own (steps below). |
| 4 | **Your firewall inventory** as an Excel `.xlsx` file in the folder you run from | You build this yourself (format below). |
| 5 | **Internet access** to `sec.cloudapps.cisco.com` and `api.fuelix.ai` | Usually already available; corporate proxies can block these. |
| 3 | **A FueliX API key** — for advisory mode | Get yours from <https://dev.fuelix.ai> — each person needs their own (steps below). |
| 4 | **Cisco PSIRT API credentials** — for inventory mode (`--api`) | A client ID and client secret from <https://developer.cisco.com/psirt/> — each person needs their own. |
| 5 | **Your firewall inventory** as an Excel `.xlsx` file in the folder you run from | You build this yourself (format below). |
| 6 | **Internet access** to `sec.cloudapps.cisco.com` and `api.fuelix.ai`, plus `apix.cisco.com` and `id.cisco.com` for `--api` | Usually already available; corporate proxies can block these. |

> Your API key is stored once, per user, via `caia --config`. The
> tool will refuse to analyze until a key is configured, and it points you to `--config`.
> Every credential is stored once, per user, via `caia --config`. You only need the ones
> for the mode you use: **advisory mode** needs the FueliX key, **inventory mode** needs the
> Cisco credentials. Each mode refuses to run without its own, and points you at `--config`.

### Get uv

Expand Down Expand Up @@ -186,6 +188,62 @@ on it behaves exactly like the uv install — use it as described in [Run it](#r

## Run it

There are **two modes**, and they answer opposite questions. Pick the one that matches what
you're asking:

| | Advisory mode (default) | Inventory mode (`--api`) |
| --- | --- | --- |
| **Question** | "Given this advisory, which of my firewalls are affected?" | "Given my inventory, which firewalls are vulnerable right now, and to what?" |
| **You supply** | An ERP or advisory link | Nothing but the inventory |
| **Answer comes from** | AI reading the advisory's text | Cisco's own published data — no AI |
| **Output** | `analysis_output_<timestamp>.xlsx` | `impact_report_<timestamp>.html` |
| **Needs** | FueliX API key | Cisco PSIRT client ID + secret |

The output formats differ because the reports are read differently. The spreadsheet is one
row per advisory, which suits a per-advisory review. The HTML report is one row per firewall
with clickable advisory links, collapsible detail and filtering — which is what a
fleet-wide worklist needs, and what a spreadsheet handles badly once a single device has
dozens of advisories against it.

### Inventory mode — check everything at once

`cd` into the folder holding your inventory, then:

```bash
caia --api
```

No link needed. The tool asks Cisco which published advisories apply to each firewall's
software release on its exact hardware model, then writes
**`impact_report_<date>_<time>.html`** into the **`output`** folder. Open it in any browser.

Every firewall gets exactly one status:

| Status | Meaning |
| ------ | ------- |
| **Impacted** | Cisco lists at least one advisory for that release on that model. The report shows each advisory's severity, its CVEs, a link to Cisco, and **the release that fixes it**. |
| **Not impacted** | Cisco confirmed it recognises both the release *and* the model, and lists no advisories. |
| **Needs review** | No trustworthy answer was possible — and the report says why. Never a claim of safety. |

**`Needs review` is the one to pay attention to.** It usually means the inventory disagrees
with Cisco's data — a release Cisco has retired as end-of-support, or a model/release pair
Cisco never shipped (e.g. ASA 9.18 on 5500-X hardware). The tool names which, so the row
becomes a data-quality fix rather than a mystery. A device that could not be checked is
never reported as safe.

Two limits are stated in every report, and worth knowing up front:

- Cisco's data holds **no advisories published before January 2022** for ASA, FMC, FTD and
FXOS. An `Impacted` list is not exhaustive, and `Not impacted` is narrower than it looks.
- A verdict does **not** know whether a vulnerable feature is switched on. `Impacted` means
"listed as affected", not "exploitable".

A run paces itself to stay inside Cisco's request quota, so a large inventory takes a few
minutes; progress is printed as it goes. Add `--dry-run` to see exactly what would be asked
without contacting Cisco at all.

### Advisory mode — check one advisory

`cd` into the folder that holds your inventory `.xlsx`, then:

```bash
Expand Down Expand Up @@ -274,7 +332,7 @@ delete that file yourself if you want it gone.

---

## Configuration (your API key)
## Configuration (your API keys)

`caia --config` stores your settings in a per-user file, so every run
in any folder can find them:
Expand All @@ -291,17 +349,44 @@ The file is `KEY=value` and looks like this (you may edit it by hand):
FUELIX_API_KEY=your-key-here
FUELIX_MODEL=claude-sonnet-5
FUELIX_BASE_URL=https://api.fuelix.ai/v1
CISCO_CLIENT_ID=your-cisco-client-id
CISCO_CLIENT_SECRET=your-cisco-client-secret
```

- **`FUELIX_API_KEY`** (required) — your personal key from the Dev Portal (steps below).
- **`FUELIX_API_KEY`** — your personal key from the Dev Portal (steps below). Needed by
advisory mode only.
- **`FUELIX_MODEL`** (optional) — chosen from a menu during `--config`; default `claude-sonnet-5`.
- **`FUELIX_BASE_URL`** (optional) — the API endpoint; **not** prompted by `--config`. Change it
only by editing the file or setting the environment variable, and only if told to.
- **`CISCO_CLIENT_ID`** / **`CISCO_CLIENT_SECRET`** — your personal Cisco PSIRT API
credentials (steps below). Needed by inventory mode (`--api`) only.

`--config` walks through everything in one pass and **every prompt is skippable** — press
Enter to leave a value alone. If you only ever use one mode, you never have to invent a
credential for the other. Each mode checks only its own credentials at run time.

Two details of the Cisco prompts worth knowing:

- The **client ID is shown as you type or paste it** (it identifies the application; it isn't
a secret), while the **client secret is never displayed**. After you enter the secret the
tool confirms *how many characters* it captured — so a truncated paste is obvious without
the value ever appearing on screen.
- `--config` then **checks the credentials against Cisco** before saving and tells you
whether they work. If Cisco rejects them you see Cisco's own reason and nothing is saved.
If Cisco simply can't be reached — a proxy or connectivity problem — it says so plainly and
offers to save them unverified, so an outage never blocks setup.

Precedence for every setting is **environment variable → config file → built-in default**, so
`FUELIX_API_KEY` / `FUELIX_MODEL` / `FUELIX_BASE_URL` in the environment still work for
automation. On macOS/Linux the config file is created owner-only (`chmod 600`). Keep it private
— it contains your key.
the same names in the environment still work for automation. On macOS/Linux the config file is
created owner-only (`chmod 600`). Keep it private — it contains your keys.

### Getting your Cisco PSIRT API credentials

1. Go to **<https://developer.cisco.com/psirt/>** and sign in with your Cisco account.
2. Register an application to get a **client ID** and **client secret** for the openVuln API.
3. Copy both, and paste them when `caia --config` asks.

Each person uses **their own** credentials — don't share them.

### Getting your FueliX API key

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.2.0
2.3.0
Loading
Loading