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
4 changes: 4 additions & 0 deletions deploy/agent_morpheus_client.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ spec:
key: mongodb.dbname
- name: DOCKER_CONFIG
value: /tmp/.docker
- name: NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: GITHUB_TOKEN
valueFrom:
secretKeyRef:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,10 @@ public Response receive(
"image": {...
}
},
"output": [...
],
"output": {
"analysis": [...],
"vex": null | {...}
},
"info": {...
},
"metadata": {...
Expand Down Expand Up @@ -304,8 +306,10 @@ public Response list(
"image": {...
}
},
"output": [...
],
"output": {
"analysis": [...],
"vex": null | {...}
},
"info": {...
},
"metadata": {...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import org.jboss.logging.Logger;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down Expand Up @@ -106,13 +105,14 @@ public Report toReport(Document doc) {
var input = doc.get("input", Document.class);
var scan = input.get("scan", Document.class);
var image = input.get("image", Document.class);
var output = doc.getList("output", Document.class);
var output = doc.get("output", Document.class);
var analysis = Objects.nonNull(output) ? output.getList("analysis", Document.class) : null;
var metadata = extractMetadata(doc);
var vulnIds = new HashSet<VulnResult>();
if (Objects.nonNull(output)) {
output.forEach(o -> {
var vulnId = o.getString("vuln_id");
var justification = o.get("justification", Document.class);
if (Objects.nonNull(analysis)) {
analysis.forEach(a -> {
var vulnId = a.getString("vuln_id");
var justification = a.get("justification", Document.class);

vulnIds.add(new VulnResult(vulnId,
new Justification(justification.getString("status"), justification.getString("label"))));
Expand Down Expand Up @@ -171,15 +171,12 @@ public void updateWithOutput(List<String> ids, JsonNode report)

Set<String> productIds = getProductId(ids);

List<Document> outputDocs = objectMapper.readValue(report.get("output").toPrettyString(),
new TypeReference<List<Document>>() {

});
Document outputDoc = objectMapper.readValue(report.get("output").toPrettyString(), Document.class);
var scan = report.get("input").get("scan").toPrettyString();
var info = report.get("info").toPrettyString();
var updates = Updates.combine(Updates.set("input.scan", Document.parse(scan)),
Updates.set("info", Document.parse(info)),
Updates.set("output", outputDocs),
Updates.set("output", outputDoc),
Updates.unset("error"));
var bulk = ids.stream()
.map(id -> new UpdateOneModel<Document>(Filters.eq(RepositoryConstants.ID_KEY, new ObjectId(id)), updates))
Expand Down Expand Up @@ -254,7 +251,7 @@ public List<Report> findByName(String name) {
"completedAt", "input.scan.completed_at",
"submittedAt", "metadata.submitted_at",
"name", "input.scan.id",
"vuln_id", "output.vuln_id");
"vuln_id", "output.analysis.vuln_id");

public PaginatedResult<Report> list(Map<String, String> queryFilter, List<SortField> sortFields,
Pagination pagination) {
Expand Down Expand Up @@ -330,14 +327,15 @@ public ProductReportsSummary getProductSummaryData(String productId) {
}
}

Object outputObj = doc.get("output");
if (outputObj instanceof List<?> outputList) {
for (Object output : outputList) {
if (output instanceof org.bson.Document outputDoc) {
String cve = outputDoc.getString("vuln_id");
Document outputDoc = doc.get("output", Document.class);
Object analysisObj = Objects.nonNull(outputDoc) ? outputDoc.get("analysis") : null;
if (analysisObj instanceof List<?> analysisList) {
for (Object analysis : analysisList) {
if (analysis instanceof org.bson.Document analysisDoc) {
String cve = analysisDoc.getString("vuln_id");
if (cve != null && !cve.isEmpty()) {
Set<Justification> justifications = cveSet.computeIfAbsent(cve, k -> new HashSet<>());
Object justificationObj = outputDoc.get("justification");
Object justificationObj = analysisDoc.get("justification");
if (justificationObj instanceof org.bson.Document justificationDoc) {
String status = justificationDoc.getString("status");
String label = justificationDoc.getString("label");
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ quarkus.rest-client.github.headers.Authorization=Bearer ${GITHUB_TOKEN}
quarkus.rest-client.github.headers.User-Agent=exploit-iq
quarkus.rest-client.morpheus.url=https://agent-morpheus:8080/generate
%dev.quarkus.rest-client.morpheus.url=http://localhost:26466/generate
quarkus.rest-client.component-syncer.url=http://job-sink.knative-eventing.svc.cluster.local/exploit-iq-nat/component-syncer
quarkus.rest-client.component-syncer.url=http://job-sink.knative-eventing.svc.cluster.local/${NAMESPACE}/component-syncer
# When building the image for production environment, need to set INCLUDE_SWAGGER_UI=false in the build environment.
# As this is a build time property, and not a runtime property ( cannot changed during runtime by just by changing the env var).
quarkus.swagger-ui.always-include=${INCLUDE_SWAGGER_UI:true}
Expand Down
60 changes: 51 additions & 9 deletions src/main/webui/src/Report.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import {
Breadcrumb,
BreadcrumbItem,
Button,
CodeBlock,
CodeBlockCode,
Divider,
EmptyState,
EmptyStateBody,
ExpandableSection,
Flex,
FlexItem,
Grid,
GridItem,
PageSection,
Expand Down Expand Up @@ -42,6 +46,7 @@ export default function Report() {
const [errorReport, setErrorReport] = React.useState({});
const [comments, setComments] = React.useState({});
const [name, setName] = React.useState();
const [vexExpanded, setVexExpanded] = React.useState(false);
const navigate = useNavigate();

React.useEffect(() => {
Expand All @@ -56,14 +61,15 @@ export default function Report() {
.catch(e => setErrorReport(e));
}, []);

const onDownload = () => {
const onDownload = (data, filename) => {
const element = document.createElement("a");
const file = new Blob([JSON.stringify(report)], {type: 'application/json'});
const file = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'});
element.href = URL.createObjectURL(file);
element.download = `${name}.json`;
element.download = filename;
document.body.appendChild(element);
element.click();
}
document.body.removeChild(element); // cleanup
};

const time_meta_fields = [
"submitted_at",
Expand Down Expand Up @@ -118,8 +124,8 @@ export default function Report() {
lines.push(`Image: ${report.input.image.name}`);
lines.push('');

if (report.output) {
report.output.forEach(vuln => {
if (report.output?.analysis) {
report.output.analysis.forEach(vuln => {
lines.push(`Vulnerability: ${vuln.vuln_id}`);
lines.push("");
if (vuln.justification?.label) {
Expand Down Expand Up @@ -188,7 +194,7 @@ export default function Report() {
}

const image = report.input.image
const output = report.output;
const analysis = report.output?.analysis;
let metadata = [];
let timestamps = [];
if (report.metadata !== undefined) {
Expand Down Expand Up @@ -238,7 +244,7 @@ export default function Report() {
</DescriptionListGroup>
</DescriptionList>

{output?.map((vuln, v_idx) => {
{analysis?.map((vuln, v_idx) => {
const uid = getUniqueId();
let userComments = '';
if(comments[vuln.vuln_id] !== undefined) {
Expand Down Expand Up @@ -279,7 +285,43 @@ export default function Report() {
</>
)
}
<DescriptionListGroup>
<DescriptionListTerm>VEX</DescriptionListTerm>
<DescriptionListDescription>
{report.output?.vex &&
<Flex columnGap={{ default: 'columnGapSm' }} alignItems={{ default: 'alignItemsFlexStart' }}>
<FlexItem>
<Button
variant="secondary"
onClick={() => onDownload(report.output.vex, `${name}_vex.json`)}
size="sm"
>
Download VEX
</Button>
</FlexItem>
<FlexItem>
<ExpandableSection
toggleText={vexExpanded ? "Hide VEX document" : "Show VEX document"}
onToggle={(_event, isExpanded) => setVexExpanded(isExpanded)}
isExpanded={vexExpanded}
isIndented
isDisabled={!report.output?.vex}
>
{report.output?.vex && (
<CodeBlock>
<CodeBlockCode>
{JSON.stringify(report.output.vex, null, 2)}
</CodeBlockCode>
</CodeBlock>
)}
</ExpandableSection>
</FlexItem>
</Flex>
}
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>

{Array.isArray(vuln.checklist) && vuln.checklist.length > 0 && (
<>
<Content component="h2">Checklist:</Content>
Expand All @@ -301,7 +343,7 @@ export default function Report() {
})}
<GridItem>
<Flex columnGap={{ default: 'columnGapSm' }}>
<Button variant="secondary" onClick={onDownload}>Download</Button>
<Button variant="secondary" onClick={() => onDownload(report, `${name}_report.json`)}>Download</Button>
<ConfirmationButton btnVariant="danger"
onConfirm={() => onDelete()}
message={`The report with id: ${name} will be permanently deleted.`}>Delete</ConfirmationButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4547,33 +4547,47 @@
}
]
},
"output": [
{
"vuln_id": "CVE-2024-44337",
"checklist": [
{
"input": "Verify Usage of `github.com/gomarkdown/markdown` Package: Check if the `github.com/gomarkdown/markdown` package is being used within the containerized application, specifically the `parser/block.go` file's paragraph function. This is the vulnerable component that could lead to an infinite loop condition.",
"response": "Unfortunately, I couldn't find any specific information on how to verify the usage of the `github.com/gomarkdown/markdown` package in a containerized application. However, in general, to verify the usage of a specific package in a containerized application, you can try to analyze the dependencies of the application, check the import statements in the code, or search for specific function or method calls related to the package. Additionally, you can try to query the vector databases containing information about the container image under investigation to check if the package is listed as a dependency.",
"intermediate_steps": null
},
{
"input": "Assess Input Handling for Markdown Parsing: Evaluate how the application handles user input for Markdown parsing. Since the vulnerability involves a specially crafted input causing an infinite loop, assess whether the application properly sanitizes and validates user input to prevent such crafted inputs from being processed.",
"response": "Based on the available information, it appears that the application does not properly sanitize and validate user input for Markdown parsing, making it vulnerable to specially crafted inputs that can cause an infinite loop. The CVE-2021-41277 vulnerability is related to this issue, and the application's input handling mechanisms do not meet the necessary security standards to prevent such attacks. Therefore, the application is likely vulnerable to this type of attack, and further investigation is needed to determine the severity of the vulnerability and potential mitigation strategies.",
"intermediate_steps": null
},
{
"input": "Review Error Handling for Infinite Loop Conditions: Check how the application handles errors related to infinite loop conditions. Given that the exploit causes the program to hang and consume resources indefinitely, assess whether the application has mechanisms in place to detect and mitigate such conditions, preventing a denial of service (DoS) scenario.",
"response": "The application's error handling for infinite loop conditions is unclear due to the lack of specific information on the CVE and its impact on the container image. However, in general, a well-designed application should have mechanisms in place to detect and mitigate infinite loop conditions, such as timeouts, resource limits, and monitoring tools to prevent a denial of service (DoS) scenario. It is recommended to review the container image's code and configuration to identify any built-in safeguards and consider implementing additional measures to prevent infinite loop conditions.",
"intermediate_steps": null
}
],
"summary": "Based on the provided Checklist and Findings, the CVE is exploitable. Specifically, Checklist Item 2: Assess Input Handling for Markdown Parsing, reveals that the application does not properly sanitize and validate user input for Markdown parsing, making it vulnerable to specially crafted inputs that can cause an infinite loop. This definitive answer indicates that the CVE is exploitable, as the application's input handling mechanisms do not meet the necessary security standards to prevent such attacks.",
"justification": {
"label": "vulnerable",
"reason": "The analysis concludes that the CVE is exploitable due to the application's failure to properly sanitize and validate user input for Markdown parsing, making it vulnerable to specially crafted inputs that can cause an infinite loop.",
"status": "TRUE"
},
"intel_score": 25
}
]
"output": {
"analysis": [
{
"vuln_id": "CVE-2024-44337",
"checklist": [
{
"input": "Is the `paragraph` function from the `github.com/gomarkdown/markdown` package called in the codebase?",
"response": "No, the `paragraph` function from the `github.com/gomarkdown/markdown` package is not called in the codebase.\n\nThe search results show that the `github.com/gomarkdown/markdown` package is imported in the codebase, but they do not provide conclusive evidence that the `paragraph` function is called. The term \"paragraph\" is used in the codebase, but it is not related to the `github.com/gomarkdown/markdown` package.",
"intermediate_steps": null
},
{
"input": "Does the application process untrusted Markdown input that could be used to trigger an infinite loop in the `paragraph` function?",
"response": "The application does process untrusted Markdown input, and there is a potential vulnerability in the `paragraph` function that could be used to trigger an infinite loop. However, the exact nature of the vulnerability and its exploitability depend on the specific implementation and usage of the `paragraph` function in the application.",
"intermediate_steps": null
},
{
"input": "Are there any input validation or sanitization mechanisms in place to prevent malicious Markdown input from reaching the `paragraph` function?",
"response": "There are no input validation or sanitization mechanisms in place to prevent malicious Markdown input from reaching the `paragraph` function. The search results suggest that there are input validation and sanitization mechanisms in place, but they appear to be related to Kubernetes API conventions, logging, and text processing, and do not directly apply to the `paragraph` function. Specifically, the `paragraph` function is not mentioned in the search results, and there is no clear connection between the function and the input validation and sanitization mechanisms found in the search results. Therefore, it is likely that the `paragraph` function is vulnerable to malicious Markdown input.",
"intermediate_steps": null
},
{
"input": "Can an attacker remotely provide Markdown input to the application, potentially allowing them to exploit the vulnerability?",
"response": "No, an attacker cannot remotely provide Markdown input to the application, potentially allowing them to exploit the vulnerability. The application uses a Markdown parser to parse Markdown documents, but the input is not user-provided and is not coming from a remote source. The `mdtoc` tool is a command-line utility that generates a table of contents for a markdown file, and it can be used in various ways, including as a console application or incorporated into markdown files directly. However, the tool does not allow remote input, and the application does not provide a way for an attacker to provide remote input.",
"intermediate_steps": null
},
{
"input": "Are there any resource limits or timeout mechanisms in place to prevent the infinite loop from causing a denial of service condition?",
"response": "There are some resource limits and timeout mechanisms in place in the container source code, such as CPU, memory, and storage limits, as well as timeout and rate limit mechanisms. However, upon closer inspection, it appears that these mechanisms are not directly related to preventing infinite loops from causing denial of service conditions. The only mention of an infinite loop is in the vendor/golang.org/x/net/html/parse.go file, where it is mentioned as a possible issue when mixing foreign content and templates, but this is not directly related to denial of service prevention. Therefore, it is unclear if there are any specific mechanisms in place to prevent infinite loops from causing denial of service conditions.",
"intermediate_steps": null
}
],
"summary": "The CVE is not exploitable in this application. Investigation confirmed that although the `github.com/gomarkdown/markdown` package is imported in the codebase, the `paragraph` function is not called. Additionally, while the application does process untrusted Markdown input, which could potentially be used to trigger an infinite loop in the `paragraph` function, the input is not user-provided and does not come from a remote source, eliminating the attack vector.",
"justification": {
"label": "code_not_reachable",
"reason": "The `github.com/gomarkdown/markdown` package is imported but the vulnerable `paragraph` function is not called, and the application's processing of untrusted Markdown input does not come from a user-provided or remote source, eliminating the attack vector.",
"status": "FALSE"
},
"intel_score": 92,
"cvss": null
}
],
"vex": null
}
}
Loading