Skip to content
Open
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
20 changes: 20 additions & 0 deletions catalog/camel-catalog/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,26 @@
</plugins>
</build>
</profile>
<profile>
<id>update-security-advisories</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.camel</groupId>
<artifactId>camel-package-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<!-- update published CVE security advisories from camel-website -->
<goal>update-security-advisories</goal>
</goals>
<phase>generate-resources</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.apache.camel.tooling.model.OtherModel;
import org.apache.camel.tooling.model.PojoBeanModel;
import org.apache.camel.tooling.model.ReleaseModel;
import org.apache.camel.tooling.model.SecurityAdvisoryModel;
import org.apache.camel.tooling.model.TransformerModel;

/**
Expand Down Expand Up @@ -663,6 +664,15 @@ default BaseModel<? extends BaseOptionModel> model(Kind kind, String name) {
*/
List<ReleaseModel> camelQuarkusReleases();

/**
* Load all published Camel CVE security advisories from catalog (the data behind
* <a href="https://camel.apache.org/security/">camel.apache.org/security</a>, synced into the catalog when it was
* built).
*
* @since 4.22
*/
List<SecurityAdvisoryModel> camelSecurityAdvisories();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: per project conventions, new public methods on API interfaces should include a @since Javadoc tag. Consider adding @since 4.22 to the method Javadoc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added @since 4.22 in 3531d80.

Claude Code on behalf of oscerd


/**
* Checks whether two endpoint URIs refer to the same logical endpoint.
* <p/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.apache.camel.tooling.model.OtherModel;
import org.apache.camel.tooling.model.PojoBeanModel;
import org.apache.camel.tooling.model.ReleaseModel;
import org.apache.camel.tooling.model.SecurityAdvisoryModel;
import org.apache.camel.tooling.model.TransformerModel;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
Expand Down Expand Up @@ -693,6 +694,25 @@ private List<ReleaseModel> camelReleases(String file) {
});
}

@Override
public List<SecurityAdvisoryModel> camelSecurityAdvisories() {
return cache("camel-security-advisories.json", () -> {
try {
List<SecurityAdvisoryModel> answer = new ArrayList<>();
InputStream is = loadResource("advisories", "camel-security-advisories.json");
String json = CatalogHelper.loadText(is);
JsonArray arr = (JsonArray) Jsoner.deserialize(json);
for (Object o : arr) {
JsonObject jo = (JsonObject) o;
answer.add(JsonMapper.generateSecurityAdvisoryModel(jo));
}
return answer;
} catch (Exception e) {
return Collections.emptyList();
}
});
}

private static boolean matchArtifact(ArtifactModel<?> am, String groupId, String artifactId, String version) {
if (am == null) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.apache.camel.tooling.model.LanguageModel;
import org.apache.camel.tooling.model.PojoBeanModel;
import org.apache.camel.tooling.model.ReleaseModel;
import org.apache.camel.tooling.model.SecurityAdvisoryModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -1752,6 +1753,25 @@ public void camelQuarkusReleases() {
Assertions.assertEquals("11", rel.getJdk());
}

@Test
public void camelSecurityAdvisories() {
List<SecurityAdvisoryModel> list = catalog.camelSecurityAdvisories();
Assertions.assertTrue(list.size() > 70);

// oldest advisory first
SecurityAdvisoryModel advisory = list.get(0);
Assertions.assertEquals("CVE-2013-4330", advisory.getCve());
Assertions.assertEquals("CRITICAL", advisory.getSeverity());

advisory = list.stream().filter(a -> a.getCve().equals("CVE-2025-27636")).findFirst().orElse(null);
Assertions.assertNotNull(advisory);
Assertions.assertEquals("MEDIUM", advisory.getSeverity());
Assertions.assertTrue(advisory.getAffected().contains("4.10.0 before 4.10.2"));
Assertions.assertTrue(advisory.getFixed().contains("4.10.2"));
Assertions.assertEquals("https://camel.apache.org/security/CVE-2025-27636.html", advisory.getUrl());
Assertions.assertTrue(advisory.getComponents().contains("camel-bean"));
}

@Test
public void testFindPojoBeanNames() {
List<String> names = catalog.findBeansNames();
Expand Down
27 changes: 24 additions & 3 deletions docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ The assistant discovers components, looks up documentation, builds a YAML route,
=== Understanding, security, and testing

* _"Explain what this route does"_ — uses `camel_route_context` for catalog-enriched analysis
* _"Analyze this route for security concerns"_ — uses `camel_route_harden_context` to detect hardcoded credentials, plain-text protocols, etc.
* _"Analyze this route for security concerns"_ — uses `camel_route_harden_context` to detect hardcoded credentials, plain-text protocols, known CVE advisories, etc.
* _"Is my Camel 4.10.1 project affected by known CVEs?"_ — uses `camel_security_advisories` to match the published Apache Camel security advisories against a Camel version or component
* _"Generate a JUnit 5 test for this route"_ — uses `camel_route_test_scaffold` to produce test class with mock endpoints and test-infra stubs

=== Error diagnosis
Expand Down Expand Up @@ -366,8 +367,28 @@ plus prompts that provide structured multi-step workflows.
| `camel_route_harden_context`
| Analyzes a route for security concerns. Identifies security-sensitive components, assigns risk levels, detects
issues like hardcoded credentials or plain-text protocols, and returns structured security findings alongside
best practices.
|===
best practices and the known published CVE advisories affecting the components used by the route at the given
Camel version.

| `camel_security_advisories`
| Lists the published Apache Camel CVE security advisories (the data behind
https://camel.apache.org/security/[camel.apache.org/security]), optionally filtered by Camel version, component
and severity. Each advisory includes the summary, affected and fixed versions, mitigation, and a best-effort
verdict on whether the given Camel version is affected. Use it to answer questions such as "is my Camel 4.10.1
project affected by known CVEs?".
|===

==== Security advisory data

The advisory data ships with the Camel catalog bundled in the MCP server, where it is synced from the official
published Apache Camel security advisories (the sources of
https://camel.apache.org/security/[camel.apache.org/security]) when Camel is built — the same way the known
releases are synced. Lookups are therefore fully offline, only published advisories are included, and the data
is as fresh as the Camel version of the MCP server: advisories published after that release are not included,
so check the web page for the very latest. When the catalog carries no advisory data the tools report it as
unavailable rather than returning an empty list, so a missing data set is never mistaken for "no known CVEs".
The advisories are also browseable as MCP resources: `camel://security/advisories` (full list) and
`camel://security/advisory/\{cve}` (detail for one CVE).

=== Error Diagnosis

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.dsl.jbang.core.commands.mcp;

import java.util.Optional;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import io.quarkiverse.mcp.server.Resource;
import io.quarkiverse.mcp.server.ResourceTemplate;
import io.quarkiverse.mcp.server.ResourceTemplateArg;
import io.quarkiverse.mcp.server.TextResourceContents;
import org.apache.camel.tooling.model.SecurityAdvisoryModel;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;

/**
* MCP Resources exposing the published Apache Camel CVE security advisories from
* <a href="https://camel.apache.org/security/">camel.apache.org/security</a> (shipped with the Camel catalog).
* <p>
* These resources provide browseable advisory data that clients can pull into context independently of route analysis.
* When the catalog carries no advisory data, the resources return an explicit {@code advisoryDataUnavailable} marker
* instead of an empty list, so a missing data set is never mistaken for "no known CVEs".
*/
@ApplicationScoped
public class AdvisoryResources {

@Inject
AdvisoryService advisoryService;

/**
* All published security advisories (one summary entry per CVE).
*/
@Resource(uri = "camel://security/advisories",
name = "camel_security_advisories_list",
title = "Published Camel CVE Security Advisories",
description = "List of all published Apache Camel CVE security advisories (the data behind "
+ "https://camel.apache.org/security/) with severity, summary, fixed versions and link. "
+ "The data ships with the Camel catalog bundled in this MCP server.",
mimeType = "application/json")
public TextResourceContents securityAdvisories() {
JsonObject result = new JsonObject();
try {
JsonArray advisories = new JsonArray();
for (SecurityAdvisoryModel advisory : advisoryService.advisories()) {
JsonObject json = new JsonObject();
json.put("cve", advisory.getCve());
json.put("severity", advisory.getSeverity());
json.put("summary", advisory.getSummary());
json.put("fixed", advisory.getFixed());
json.put("url", advisory.getUrl());
advisories.add(json);
}
result.put("advisories", advisories);
result.put("totalCount", advisories.size());
result.put("source", AdvisoryService.SECURITY_PAGE_URL);
} catch (Exception e) {
result.put("advisoryDataUnavailable", true);
result.put("message", e.getMessage());
}
return new TextResourceContents("camel://security/advisories", result.toJson(), "application/json");
}

/**
* Full detail for a single published security advisory.
*/
@ResourceTemplate(uriTemplate = "camel://security/advisory/{cve}",
name = "camel_security_advisory_detail",
title = "Camel CVE Security Advisory Detail",
description = "Full detail for a single published Apache Camel security advisory (by CVE id, "
+ "e.g. CVE-2025-27636) including affected and fixed versions, mitigation and "
+ "affected components.",
mimeType = "application/json")
public TextResourceContents securityAdvisoryDetail(
@ResourceTemplateArg(name = "cve") String cve) {

String uri = "camel://security/advisory/" + cve;
String wanted = cve == null ? "" : cve.trim();

JsonObject result = new JsonObject();
try {
Optional<SecurityAdvisoryModel> found = advisoryService.advisories().stream()
.filter(advisory -> advisory.getCve().equalsIgnoreCase(wanted))
.findFirst();

if (found.isEmpty()) {
result.put("cve", cve);
result.put("found", false);
result.put("message", "No published Apache Camel security advisory found for '" + cve + "'. "
+ "See " + AdvisoryService.SECURITY_PAGE_URL + " for the full list.");
} else {
SecurityAdvisoryModel advisory = found.get();
result.put("cve", advisory.getCve());
result.put("found", true);
result.put("severity", advisory.getSeverity());
result.put("summary", advisory.getSummary());
result.put("affected", advisory.getAffected());
result.put("fixed", advisory.getFixed());
result.put("mitigation", advisory.getMitigation());
result.put("date", advisory.getDate());
result.put("url", advisory.getUrl());
JsonArray components = new JsonArray();
components.addAll(advisory.getComponents());
result.put("components", components);
}
} catch (Exception e) {
result.put("advisoryDataUnavailable", true);
result.put("message", e.getMessage());
}
return new TextResourceContents(uri, result.toJson(), "application/json");
}
}
Loading