Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
bc9d715
Add VEX generation/advisor and Blueprint connection-ID resolution
planetlevel Sep 2, 2026
d14f881
Enrich VEX with description, recommendation, response, EPSS/CISA KEV …
planetlevel Sep 2, 2026
9e284d3
Add samples/ with real output from every generator + advisor report
planetlevel Sep 2, 2026
5cfd192
Scope all samples to a single app per report type
planetlevel Sep 2, 2026
33de63c
Rework VEX Advisor report: merge duplicate CVE lists, summary-first l…
planetlevel Sep 2, 2026
a6e30fe
Merge VEX Advisor's per-app tables into one compressed table with cod…
planetlevel Sep 3, 2026
6b8d5ec
Add explicit Action column (Safe/Monitor/Review) to VEX Advisor table
planetlevel Sep 3, 2026
c35228f
Bump GitHub Actions to Node 24-native versions
planetlevel Sep 3, 2026
513d736
Spell out Rationale, add day counts, drop the Action column
planetlevel Sep 3, 2026
b34cf8c
Clarify Rationale wording: Library Unused / CVE Shielded / CVE Not Us…
planetlevel Sep 3, 2026
c7d00a6
Report Assess/Protect module enablement per app/environment in VEX
planetlevel Sep 3, 2026
651ea7d
Rename Protect->ADR, detect real CVE Shield availability, fix a dropp…
planetlevel Sep 3, 2026
0999309
Never claim not_affected via duration when CVE Shield has no coverage
planetlevel Sep 3, 2026
e59a4d5
Give the shield-unavailable in_triage case its own rationale word
planetlevel Sep 3, 2026
e08e249
Simplify shieldAvailability to the org-wide fact, drop per-env inference
planetlevel Sep 3, 2026
b5ee8a7
VEXGenerator: exclude CVEs with no Shield coverage entirely, don't cl…
planetlevel Sep 3, 2026
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
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
Expand All @@ -23,7 +23,7 @@ jobs:
run: mvn clean package

- name: Upload jar artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: runtime-analyst-jar
path: target/runtime-analyst-*.jar
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
build-and-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ CLAUDE.md
cbom*.json
aibom*.json
blueprint*.json
vex*.json

# Shell scripts (may contain credentials)
*.sh
Expand Down
222 changes: 94 additions & 128 deletions README.md

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions samples/sample-aibom-advisor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!-- Contrast AI Advisor Report -->

# Contrast AI Advisor
## Inventory of AI-Enabled Applications

---

**Client:** Robert-cargocats-aiservice
**Report Date:** September 2, 2026
**Assessment Type:** Runtime AI/LLM Usage Inventory & Governance Risk Assessment

---

## Executive Summary

This report inventories every AI/LLM model and provider observed actually running in production across your applications - the model, provider, destination endpoint, and real call stack behind each usage, captured by Contrast Security's runtime instrumentation.

**1** application(s) use AI, calling **1** distinct model(s) across **1** provider(s), for **1** total usage instance(s).

> No applications were flagged CRITICAL or HIGH risk for their AI usage.

### Applications

| Application | Risk Level | Models Used |
|-------------|------------|--------------|
| Robert-cargocats-aiservice | LOW | `smollm2:135m-tuned` |

### Models & Providers

| Provider | Model | Host Category | Applications | Invocations |
|----------|-------|----------------|---------------|-------------|
| openai | `smollm2:135m-tuned` | local | 1 | 2 |

- **1** model(s) self-hosted/local (no external data egress)

| Risk Level | Applications |
|------------|--------------|
| LOW | 1 |

---

## Application Inventory

### Robert-cargocats-aiservice

**Risk Level:** LOW

**Language:** JAVA | **Posture Score:** 7.4 (HIGH) | **Open Issues:** 7 | **Connects To:** Robert-cargocats-frontgateservice

Robert-cargocats-aiservice is a Java Spring Boot microservice that appears to provide AI/chat functionality as a backend for the Robert-cargocats-frontgateservice gateway. It exposes an endpoint that accepts requests and forwards them to a language model via an OpenAI-compatible client, acting as an internal AI integration layer within the CargoCats application suite.

**Risk Rationale:** The model (smollm2:135m-tuned) is served locally via Ollama at an internal hostname, so no data leaves the environment to a third-party provider. This is self-hosted inference reachable only from an internal gateway service, which limits data exposure risk even though the call path is a straightforward, unauthenticated-looking pass-through.

**Recommendation:** Confirm the /openai endpoint has appropriate authentication and input validation, and add monitoring/logging for prompt content to catch any sensitive data being passed through, since local hosting reduces but doesn't eliminate governance blind spots.

#### AI Usage

| Attribute | Value |
|-----------|-------|
| **Model** | `smollm2:135m-tuned` |
| **Provider** | openai |
| **Endpoint** | `http://ollama:11434/v1` |
| **Host Category** | local |
| **Route** | unknown |
| **Frequency (model-wide)** | Very Low (2 invocations across all apps using this model) |
| **Reachability (this app)** | 1 code path(s) in this application |

**What it's doing:** AiController.openai handles an incoming request and delegates to AiService.chat, which calls the OpenAI-compatible ChatCompletionService.create client method against a local Ollama instance. This generates a chat completion response for the controller's endpoint, likely relaying a user or gateway-supplied prompt to the locally hosted model.

---

## Appendix: Methodology

AI/LLM usage data collected via Contrast Security runtime instrumentation. Application descriptions and connection data are derived from the Contrast architecture graph (application, server, and library relationships); AI usage descriptions are inferred from the real stack trace captured at each call site.

- **CRITICAL**: Likely sensitive/regulated data sent to an unvetted third-party model
- **HIGH**: Production cloud AI usage without an apparent governance process
- **MEDIUM**: Approved-looking usage lacking monitoring, or non-production usage that could reach production
- **LOW**: Local/self-hosted usage or clearly low-sensitivity usage
- **NOT_AI_RISK_ISSUE**: Benign, well-governed usage with no identifiable risk signal

---

*Report generated by Contrast AI Advisor*
*Powered by Contrast Security Runtime Observability*
114 changes: 114 additions & 0 deletions samples/sample-aibom.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:736dae4b-836e-4f0a-a706-3a5d9519923b",
"version": 1,
"metadata": {
"timestamp": "2026-09-02T21:55:47Z",
"component": {
"type": "application",
"bom-ref": "robert-cargocats-aiservice",
"name": "Robert-cargocats-aiservice",
"version": "1.0"
}
},
"components": [
{
"type": "machine-learning-model",
"bom-ref": "ai-openai-smollm2-135m-tuned",
"publisher": "openai",
"name": "smollm2:135m-tuned",
"properties": [
{
"name": "contrast:usageCount",
"value": "2"
},
{
"name": "contrast:uniqueLocations",
"value": "1"
},
{
"name": "contrast:provider",
"value": "openai"
},
{
"name": "contrast:endpoint",
"value": "http://ollama:11434/v1"
},
{
"name": "contrast:hostCategory",
"value": "local"
}
],
"evidence": {
"occurrences": [
{
"location": "com.openai.services.blocking.chat.ChatCompletionServiceImpl$WithRawResponseImpl.create(ChatCompletionServiceImpl.kt)",
"additionalContext": "App: Robert-cargocats-aiservice Stack Trace: com.openai.services.blocking.chat.ChatCompletionServiceImpl$WithRawResponseImpl.create(ChatCompletionServiceImpl.kt) com.openai.services.blocking.chat.ChatCompletionServiceImpl.create(ChatCompletionServiceImpl.kt:63) com.openai.services.blocking.chat.ChatCompletionService.create(ChatCompletionService.kt:64) com.contrast.aiservice.AiService.chat(AiService.java:54) com.contrast.aiservice.AiController.openai(AiController.java:30) java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) java.base/java.lang.reflect.Method.invoke(Unknown Source) org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:114) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:732) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398) org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740) org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189) org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658) org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) java.base/java.lang.Thread.run(Unknown Source)"
}
]
}
},
{
"type": "application",
"bom-ref": "app-robert-cargocats-aiservice",
"name": "Robert-cargocats-aiservice",
"externalReferences": [
{
"type": "runtime-analysis-report",
"url": "https://eval.contrastsecurity.com/Contrast/cs/index.html#/6a3073a5-5e68-40e5-9d7d-165340fff15a/explorer?detailsId\u003d-606870275\u0026applicationId\u003d5e888a9b-a094-490a-ab35-01eec6afcb50",
"comment": "Contrast Application Explorer"
}
],
"properties": [
{
"name": "contrast:language",
"value": "JAVA"
},
{
"name": "contrast:postureScore",
"value": "7.4"
},
{
"name": "contrast:postureSeverity",
"value": "HIGH"
},
{
"name": "contrast:criticality",
"value": "3"
},
{
"name": "contrast:openIssuesTotal",
"value": "7"
},
{
"name": "contrast:serverCount",
"value": "1"
},
{
"name": "contrast:libraryCount",
"value": "56"
},
{
"name": "contrast:connectedApplications",
"value": "Robert-cargocats-frontgateservice"
}
],
"description": "Robert-cargocats-aiservice is a Java Spring Boot microservice that appears to provide AI/chat functionality as a backend for the Robert-cargocats-frontgateservice gateway. It exposes an endpoint that accepts requests and forwards them to a language model via an OpenAI-compatible client, acting as an internal AI integration layer within the CargoCats application suite."
}
],
"dependencies": [
{
"ref": "app-robert-cargocats-aiservice",
"dependsOn": [
"ai-openai-smollm2-135m-tuned"
]
},
{
"ref": "robert-cargocats-aiservice",
"dependsOn": [
"ai-openai-smollm2-135m-tuned"
]
}
]
}
97 changes: 97 additions & 0 deletions samples/sample-blueprint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"$schema": "https://cyclonedx.org/schema/2.0/cyclonedx-2.0.schema.json",
"specFormat": "CycloneDX",
"specVersion": "2.0",
"serialNumber": "urn:uuid:c0876091-e05b-4bfb-bea5-e76d5c88112b",
"version": 1,
"metadata": {
"timestamp": "2026-09-02T21:56:06.331Z"
},
"blueprints": [
{
"bom-ref": "blueprint-1",
"name": "Blueprint - Cargo-Crypto-contrast-cargo-cats-frontgateservice",
"description": "Generated from Contrast runtime observability data: application architecture/connections (contrast-graph) and crypto/AI usage observations, mapped onto the CycloneDX behavior taxonomy. Does not include threats, controls, or risks - see TM-BOM.",
"modelTypes": [
"architecture",
"behavioral"
],
"assets": [
{
"bom-ref": "asset-app-cargo-crypto-contrast-cargo-cats-frontgateservice",
"name": "Cargo-Crypto-contrast-cargo-cats-frontgateservice",
"type": "system",
"zone": "zone-env-development"
},
{
"bom-ref": "asset-external-robert-cargocats-frontgateservice",
"name": "Robert-cargocats-frontgateservice",
"type": "system",
"description": "Known only as an architecture-graph connection target; no application-level data available."
},
{
"bom-ref": "asset-external-robert-cargocats-dataservice",
"name": "Robert-cargocats-dataservice",
"type": "system",
"description": "Known only as an architecture-graph connection target; no application-level data available."
},
{
"bom-ref": "asset-external-robert-cargocats-reportservice",
"name": "Robert-cargocats-reportservice",
"type": "system",
"description": "Known only as an architecture-graph connection target; no application-level data available."
},
{
"bom-ref": "asset-external-robert-cargocats-aiservice",
"name": "Robert-cargocats-aiservice",
"type": "system",
"description": "Known only as an architecture-graph connection target; no application-level data available."
}
],
"zones": [
{
"bom-ref": "zone-env-development",
"name": "DEVELOPMENT",
"type": "deployment"
}
],
"flows": [],
"behaviors": {
"instances": [
{
"bom-ref": "behavior-1",
"behavior": "security:cryptography:encryptsData",
"actors": [
"asset-app-cargo-crypto-contrast-cargo-cats-frontgateservice"
],
"trigger": "unknown"
},
{
"bom-ref": "behavior-2",
"behavior": "security:cryptography:hashesData",
"actors": [
"asset-app-cargo-crypto-contrast-cargo-cats-frontgateservice"
],
"trigger": "unknown"
},
{
"bom-ref": "behavior-3",
"behavior": "security:cryptography:hashesData",
"actors": [
"asset-app-cargo-crypto-contrast-cargo-cats-frontgateservice"
],
"trigger": "unknown"
},
{
"bom-ref": "behavior-4",
"behavior": "security:cryptography:hashesData",
"actors": [
"asset-app-cargo-crypto-contrast-cargo-cats-frontgateservice"
],
"trigger": "unknown"
}
]
}
}
]
}
Loading
Loading