Runtime Analyst turns Contrast Security's runtime observability data into structured, standard reports. RA doesn't scan source code or dependency manifests - it reads what Contrast's agents observed actually running in your applications, with full stack traces, usage counts, and architecture context. This runtime visibility is critical for post-quantum migration and AI governance planning - you need to know not just what's in use, but how it's being used and by what.
- Cryptography - every cryptographic algorithm observed running in your apps/APIs, with NIST post-quantum vulnerability classification. Produces a CBOM and an AI-powered analysis report.
- AI - every AI model and provider observed running, with cloud-vs-local classification for shadow-AI visibility. Produces a AIBOM and an AI-powered analysis report
- Blueprint (alpha) - a map of how your apps/APIs connect and behave: assets, deployment zones, connections, and crypto/AI behaviors
- Threat Model (coming soon) - TBD
- Vulnerability Exclusion - an analysis of library CVE exposure in your apps/APIs, based Contrast's runtime library-usage and CVE Shield data. Produces a VEX document and an AI-powered analysis report.
Requirements:
- A Contrast account with runtime data already flowing in from real-world applications and APIs - Contrast's agents must actually be deployed and observing traffic. Runtime Analyst only reports on what Contrast has observed; it has nothing to show against an account with no instrumented applications or no production/QA traffic.
- The
claudeCLI on yourPATHand logged in, for--analyze/the advisor reports - no separate API key, no AWS/Bedrock credentials, no Python.
Contrast provides runtime observability that goes far beyond static code scanning or self-reported inventories:
- Full-stack inventory - Captures crypto and AI usage across your entire application stack at runtime, not just what's declared in source code or dependency manifests
- Complete details - Algorithm strength/mode/padding/OIDs for crypto; provider/model/endpoint for AI
- Usage metrics - How often each crypto algorithm or AI model is actually called in production
- Full stack traces - Understand the context: is this crypto protecting passwords, SSL/TLS, tokens? Is this AI call generating a chat reply, summarizing a document?
- Multiple call paths - See how many different code paths invoke each algorithm/model
- Architecture context - Each application component is enriched with its language, security posture, and what it's connected to (from the Contrast architecture graph), so the advisor reports can describe what an application actually is, not just what it uses
- Application dependencies - Which apps and APIs depend on each crypto algorithm or AI model
- Real connections, not guesses - Blueprint's assets, zones, and flows come from the same architecture graph, so the map it draws is what Contrast actually saw talking to what, not an inferred or self-reported topology
Every command except auth itself reads a contrast.properties file for credentials. If cbom, aibom, blueprint, or vex don't find one (or the one named with -c), they run auth for you automatically first, then proceed with the command you actually asked for - so you never have to run auth yourself as a separate step. You can also set up contrast.properties ahead of time, two ways:
Option 1 - auth (recommended):
java -jar runtime-analyst.jar auth --host https://your-instance.contrastsecurity.comEvery command accepts -c <path> to point at a config file somewhere other than the working directory.
mvn clean package # build target/runtime-analyst-1.0.jarA single jar, dispatched by subcommand:
| Subcommand | Purpose |
|---|---|
auth |
Log in via browser and generate contrast.properties |
cbom |
Generate a Cryptography Bill of Materials |
aibom |
Generate an AI/LLM usage Bill of Materials |
blueprint (alpha) |
Generate a draft CycloneDX 2.0 Architectural BOM + Bill of Behaviors |
vex |
Generate a CycloneDX VEX for an application's library CVEs |
cbom-advisor |
Re-run the Quantum Advisor against an existing CBOM file |
aibom-advisor |
Re-run the AI Advisor against an existing AI-BOM file |
vex-advisor |
Re-run the VEX Advisor against an existing VEX file |
cbom, aibom, blueprint, and vex all share --app <id|name>, --env <PRODUCTION|DEVELOPMENT|QA>, --list (list available applications and exit), -o <file> (output path), and -c <config.properties>. For cbom/aibom/blueprint, --env filters which observations are included. For vex, --env means something more specific: it scopes each claim to that one environment's CVE Shield/exposure status, instead of considering the application's dev/qa/prod combined (see the vex policy below) - --env PRODUCTION means "not seen/protected in production specifically," not "not seen somewhere across the app." cbom/aibom/vex additionally support --analyze, which runs the matching advisor automatically after generation. vex also has its own --vex-accept-after-days <n> (see below).
$ java -jar runtime-analyst.jar auth -h
# First-time setup against your instance
$ java -jar runtime-analyst.jar auth --host https://eval.contrastsecurity.com
# Write to a different config path
$ java -jar runtime-analyst.jar auth --host https://eval.contrastsecurity.com -o prod.properties
$ java -jar runtime-analyst.jar cbom -h
java -jar runtime-analyst.jar cbom # all apps -> cbom.json
java -jar runtime-analyst.jar cbom --list # list applications and their IDs
java -jar runtime-analyst.jar cbom --app "MyApp" # filter by app name
java -jar runtime-analyst.jar cbom --app 7136cb1b-f846-4c1d-bdd3-77b448cbd2fe
java -jar runtime-analyst.jar cbom --env PRODUCTION # only prod observations
java -jar runtime-analyst.jar cbom --app "MyApp" --env PRODUCTION -o myapp-prod.json
java -jar runtime-analyst.jar cbom -c prod.properties --list
java -jar runtime-analyst.jar cbom --analyze # + Quantum Advisor risk report
$ java -jar runtime-analyst.jar aibom -h
java -jar runtime-analyst.jar aibom
java -jar runtime-analyst.jar aibom --list
java -jar runtime-analyst.jar aibom --app "MyApp" --env PRODUCTION
java -jar runtime-analyst.jar aibom --analyze # + AI Advisor governance report
$ java -jar runtime-analyst.jar blueprint -h
java -jar runtime-analyst.jar blueprint
java -jar runtime-analyst.jar blueprint --app "MyApp" --env PRODUCTION
$ java -jar runtime-analyst.jar vex -h
java -jar runtime-analyst.jar vex --app "MyApp" # -> vex-MyApp.json, considers dev+qa+prod together
java -jar runtime-analyst.jar vex --app "MyApp" --env PRODUCTION # scope every claim to production only
java -jar runtime-analyst.jar vex --list # list applications and their IDs
java -jar runtime-analyst.jar vex --app "MyApp" --vex-accept-after-days 60 -o vex.json
java -jar runtime-analyst.jar vex --app "MyApp" --analyze # + VEX Advisor soundness review
By default a claim considers the application's dev/qa/prod environments together - "protected" means protected in at least one, "not seen" means not seen in any of them. --env <tier> narrows every claim to just that one environment instead, so --env PRODUCTION means "not seen/protected in production specifically," not "not seen somewhere in the app." An exclusion is generated when:
- Library never loaded at runtime in this application (
classes_used == 0for that app) →not_affected - Library loaded, but CVE Shield is actively mitigating it for this application
→
not_affected/protected_at_runtime. - Library loaded, and this application's CVE status is
EXPOSED/EXPLOITED→ no VEX statement at all. This tool never suppresses a vulnerability it can't positively account for. - Library loaded, but the CVE has never been observed executing in this application - a statement is still generated, but what it claims depends on whether CVE Shield has a virtual patch for that CVE at all. This is a per-CVE, product-level fact (
cveShieldExistson the org-wide/organizations/{org}/cvesendpoint) - not an app- or environment-scoped one, since coverage existing anywhere means it's available everywhere Shield/ADR is enabled:- Shield covers this CVE and simply hasn't fired →
not_affectedonce days observed clears the acceptance threshold,in_triageuntil then - elapsed time is meaningful evidence here, because something was watching. - Shield has no coverage at all for that CVE →
in_triage, permanently, regardless of days observed. Elapsed time can't turn "no detector was watching" into "nothing happened" - this can never graduate tonot_affectedon duration alone. Earlier versions of this tool tried three different wrong things here: silently dropping these CVEs entirely, marking themnot_affectedanyway once enough days had passed, and inferring "coverage" from a per-app/per-environmentNO_SHIELDstatus instead of the org-wide fact - all three were bugs, not policy choices.
- Shield covers this CVE and simply hasn't fired →
Every statement carries contrast:* properties (classesUsed/classCount, daysObserved, acceptAfterDays, envFilter, and the per-environment devStatus/qaStatus/prodStatus) so a reviewer can see the underlying evidence, not just the resulting state.
- Application components with dependencies on crypto algorithms, enriched with
contrast:language/postureScore/connectedApplications/etc. and a deep-linkexternalReference - Cryptographic asset components with:
- Algorithm properties (primitive, mode, padding)
- OID (Object Identifier)
- Classical security level
- NIST quantum security level (0 = quantum vulnerable)
contrast:usageCount/contrast:uniqueLocations- Full stack traces showing usage context (passwords, SSL, etc.), deduplicated per-application
Contrast Crypto Inventory
├── app-frontend → SHA-256, AES/GCM
└── app-backend → SHA-256, MD5 (quantum vulnerable), RSA
- Application components, enriched the same way as the CBOM's
machine-learning-modelcomponents (one per provider/model observed) with:contrast:provider/contrast:endpoint/contrast:hostCategory(cloud,local, orunknown)contrast:usageCount/contrast:uniqueLocations- Full stack traces, deduplicated per-application
Contrast AI Usage Inventory
├── app-aiservice → openai/gpt-4o (cloud)
└── app-reportservice → openai/smollm2:135m-tuned (local, via Ollama)
A draft CycloneDX 2.0 document with a top-level blueprints[] array containing:
assets[]- one per application (from thecontrast-grapharchitecture graph) plus one per connection known only by namezones[]- one per deployment tierflows[]- architecture-graph connections between assets, deduplicated and modeled as bidirectional since the graph API doesn't preserve directionbehaviors.instances[]- crypto/AI usage observations mapped onto the CycloneDX behavior taxonomy (e.g.security:cryptography:encryptsData,ai:generative:processesPrompt)
Deliberately does not generate threats, controls, or risks (TM-BOM) - the draft spec models those as a separate, sibling construct, and none of it can be derived from Contrast telemetry without an actual STRIDE-style analysis.
A standard CycloneDX 1.6 document with a top-level vulnerabilities[] array, one entry per (application, library, CVE) with:
id- the CVE identifier,source- NVD referencedescription- the CVE description, as reported by Contrastratings[]- CVSS v3.1 score/severity/vector as reported by Contrastadvisories[]- any reference URLs Contrast has on file for the CVE (omitted when there are none)affects[].ref- a best-effortpkg:maven/...purl for the affected libraryaffects[].versions[]- the deployed version (affected) and, when Contrast has upgrade guidance for the library, the recommended fixed version (unaffected)recommendation- the remediation action: Contrast's own minimal-upgrade guidance for the library when available, falling back to "no newer release identified" when it isn'tanalysis.state/analysis.justification/analysis.detail- the VEX claim itself and why it was made (see the policy in thevexexamples above)analysis.response[]-updatewhen a fix version is known,workaround_availableforprotected_at_runtimeclaims (the active CVE Shield control is the workaround), omitted otherwiseproperties[]- the underlying evidence (contrast:classesUsed/classCount,contrast:daysObserved,contrast:acceptAfterDays,contrast:devStatus/qaStatus/prodStatus,contrast:latestVersion) plus exploitability signals (contrast:epssScore/epssPercentile,contrast:cisaKev- CISA Known Exploited Vulnerabilities catalog membership) pluscontrast:shieldAvailable- whether CVE Shield could catch this specific CVE at all in the environment(s) considered ("true"/"false"/absent if unknown)
All of the above is deterministic, pulled directly from Contrast's own CVE/library data - nothing here is AI-generated, since a VEX claim is an attestation and needs to stay auditable back to its source evidence. The EPSS/CISA KEV/Shield-availability signals are new inputs to the VEX Advisor's AI judgment (below), not to the claim itself.
contrast:shieldAvailable is the org-wide cveShieldExists flag from /organizations/{org}/cves, verbatim - whether Contrast has a virtual patch definition for this CVE at all, a per-CVE product-level fact rather than something that could differ between environments. (An earlier version of this tool tried to infer it from the per-application cves/issues endpoint's NO_SHIELD status per environment instead - that status is real and does show up per-app/per-env, but coverage itself isn't scoped that way, so the org-wide fact is authoritative.) This isn't just informational - it changes which state a claim can reach (see policy rule 4 above): when Shield has no coverage, the claim stays in_triage forever, since elapsed time without a detector watching is not evidence of anything.
The document also has a top-level components[] array with one Application component per app, carrying whether Assess (the module that produces every claim's runtime evidence) and ADR (the classic HTTP-rule-based RASP module, formerly branded "Protect" - a different, older product from CVE Shield) are enabled per environment: contrast:assessEnabledDev/Qa/Prod and contrast:adrEnabledDev/Qa/Prod ("true"/"false"/"" - empty means no agent was ever seen reporting from that environment, which is a different fact from "disabled").
Contrast VEX
└── SAML-PetClinic-Demo
├── CVE-2018-14721 (jackson-databind 2.8.8) → not_affected / code_not_reachable
└── CVE-2022-22965 (spring-webmvc 4.3.9) → not_affected (288 days, no observed execution)
Note: PROTECTED_AT_RUNTIME (CVE Shield actively mitigating) is implemented but not yet confirmed against a live example with that status - see the caveats in the vex command's own help/design notes before relying on it.
- Quantum Advisor - findings grouped by risk level (CRITICAL/HIGH/MEDIUM/LOW/NOT_QUANTUM_ISSUE), with an "Application Context" section describing each app from its architecture graph data
- AI Advisor - organized as an inventory of AI-enabled applications (one section per app, not per finding): an AI-generated description of what the app does, then each AI usage instance with model/provider/endpoint and a description of what that specific call is doing, inferred from the key methods around it in the stack trace
- VEX Advisor - not a second opinion on whether a CVE exists (Contrast's runtime data already establishes that), but a soundness check on whether each
not_affected/in_triageclaim is safe to rely on given the CVE's severity/exploitability. Which claims need review is decided deterministically, not by an AI call - CRITICAL/HIGH severity, CISA KEV listing, EPSS ≥ 0.5, or Assess having no runtime data to back the claim are all plain facts already sitting on the VEX, so classifying 235 statements one-by-one over an AI round-trip added minutes of latency and cost without adding judgment, and made the same VEX produce a different flagged count from run to run on identical input (21, 61, 125 in one session) - a real liability for something meant to be relied on. The single AI call per app is used only for the narrative (a 2-3 sentence description, risk level, risk rationale, and recommendation, given the already-decided flagged list) - turning facts into prose, not deciding what the facts are. This cut the advisor's typical run from ~8 minutes and ~30,000 output tokens down to under 30 seconds and ~2,000. The report opens with a summary (coverage + computed key findings: flagged-claim count, distinct KEV/high-EPSS CVEs among them, CRITICAL/HIGH apps) before the per-application detail, and lists every claim in one compressed table per app (CVE | Library | Score | VEX | Shield | Rationale, e.g.Yes/CVE Not Used 288dorNo/CVE Not Used 288d- decoded in a legend) instead of a full-sentence rationale repeated per claim. The Shield column iscontrast:shieldAvailable-Nomeans the claim rests entirely on absence-of-execution with no possible active backstop, which is materially weaker than the same claim where Shield exists and simply hasn't fired (and CVEs withNohere never actually reach the VEX at all - see thevexcommand's policy above). Rows sort CISA KEV-listed first, then by EPSS, then by CVSS score, so the claims worth a second look surface at the top. Each app's section also shows a Protection Status line (Assess/ADR enablement per environment, from the VEX's owncomponents[]- see above): a duration-based claim in an environment where Assess has no data isn't weak evidence, it's no evidence, and is flagged for review on that basis alone.
Both the Quantum and AI Advisors write their generated application descriptions back into the source BOM's Component.description field, so the BOM itself stays self-describing even without the report. The Quantum Advisor also writes quantum:* risk properties (risk level, recommendation, code source, etc.) back onto each crypto algorithm component. The VEX Advisor writes contrast:vexAdvisorAssessment (sound/needs_review) and contrast:vexAdvisorRationale back onto each vulnerability's properties[]. All of this happens automatically as part of every --analyze run, no separate step needed.
tools/bom-viewer.html is a standalone, offline browser tool for viewing a CBOM or AI-BOM: drag and drop (or click to browse for) a JSON file and it detects which kind of BOM it is and renders it accordingly - no server or build step required.
- CBOM - crypto algorithm components as readable cards, including any
quantum:*risk properties the advisor wrote back - AI-BOM - an application-by-application view (model, provider, endpoint, host category, stack trace) plus each app's
Component.description, mirroring the AI Advisor report
open tools/bom-viewer.htmlTwo sample AI-BOM files are included to try it with:
sample-aibom.json- real output fromAIBOMGeneratoragainst a live org (one model, both apps local/self-hosted)test-aibom.json- a hand-crafted fixture covering cases the sample doesn't: multiple providers (OpenAI/Anthropic/Ollama), both cloud and local host categories, and an app with multiple call sites for the same model
samples/ has one real, current output from each generator and its matching advisor report - useful as a reference for what each format actually looks like without running the tool yourself:
| CycloneDX document | Advisor report | Scoped to |
|---|---|---|
sample-cbom.json |
sample-cbom-advisor.md |
Cargo-Crypto-contrast-cargo-cats-frontgateservice |
sample-aibom.json |
sample-aibom-advisor.md |
Robert-cargocats-aiservice |
sample-blueprint.json |
(no advisor - see the Blueprint section above) | Cargo-Crypto-contrast-cargo-cats-frontgateservice |
sample-vex.json |
sample-vex-advisor.md |
SAML-PetClinic-Demo |
Each is scoped with --app to one application rather than the whole org (smaller, more readable files, and a full-org VEX run makes one CVE-issues API call per application). They're deliberately not all the same app - in this org, crypto usage, AI usage, and vulnerable libraries happen to show up in three disjoint sets of applications, so no single app would produce non-empty output for all four generators. Each sample uses whichever app actually has real data for that report type.
| Property | Description |
|---|---|
contrast.url |
Contrast API base URL |
contrast.org_id |
Your organization ID |
contrast.auth_header |
Base64 encoded email:service_key |
contrast.api_key |
Your API key |
The tool automatically parses algorithm strings straight from the JCA Cipher/MessageDigest/etc. constructor (e.g., AES/GCM/NoPadding, RSA/ECB/OAEPWithSHA-256AndMGF1Padding, PBEWithMD5AndDES) and extracts:
- Algorithm family (AES, RSA, SHA, DES/3DES, PBE, etc.)
- Mode (GCM, CBC, ECB, CTR, etc.) - omitted for asymmetric algorithms, where a "mode" segment in the transformation string is a JCA naming artifact, not a real cryptographic mode
- Padding scheme
- Key size
- Cryptographic primitive type
- Security levels
Algorithms are classified by NIST quantum security level:
- Level 0: Quantum vulnerable (RSA, ECDSA, ECDH, etc.) - also used as a catch-all for classically-broken algorithms (MD5, SHA-1, DES) that the advisor then re-classifies as
NOT_QUANTUM_ISSUE - Level 1-5: Quantum resistant (AES-128+, SHA-256+, ML-KEM, ML-DSA, etc.)
Copyright Contrast Security