Skip to content

Commit

Permalink
Merge branch 'main' into fediverse
Browse files Browse the repository at this point in the history
  • Loading branch information
cedricbonhomme committed Nov 13, 2024
2 parents 7244db7 + 69c263c commit 8d5dc2d
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 26 deletions.
9 changes: 7 additions & 2 deletions website/web/api/v1/sighting.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from website.validators import validate_json
from website.web.api.v1.common import auth_func
from website.web.api.v1.common import metada_params_model
from website.web.api.v1.common import user_light_params_model
from website.web.api.v1.common import user_params_model
from website.web.api.v1.common import uuid_type
from website.web.api.v1.types import ResultType
from website.models import Sighting
Expand Down Expand Up @@ -102,6 +102,10 @@
sighting = sighting_ns.model("Sighting", sighting_params_model)
metadata = sighting_ns.model("metadata", metada_params_model)

sighting["author"] = fields.Nested(
sighting_ns.model("User", user_params_model), readonly=True
)

sighting_list_fields = sighting_ns.model(
"SightingsList",
{
Expand Down Expand Up @@ -226,7 +230,8 @@ def post(self) -> Tuple[ResultType, int]:
)

if (
Sighting.query.filter(
sighting.get("source", False)
and Sighting.query.filter(
Sighting.vulnerability.ilike(sighting["vulnerability"]),
Sighting.source == sighting["source"],
# func.date(Sighting.creation_timestamp) == func.date(current_time),
Expand Down
1 change: 1 addition & 0 deletions website/web/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,4 @@ def get_sri(directory: str, filename: str) -> str:
application.jinja_env.filters["cvss_severity"] = jinja_filters.cvss_severity
application.jinja_env.filters["cvss_clean_vector"] = jinja_filters.cvss_clean_vector
application.jinja_env.filters["update_query"] = jinja_filters.update_query_params
application.jinja_env.filters["is_url"] = jinja_filters.is_url
15 changes: 15 additions & 0 deletions website/web/filters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3

import re
import hashlib
import json
from datetime import datetime
Expand All @@ -12,6 +13,20 @@
import markdown # type: ignore[import-untyped]


def is_url(value: str) -> bool:
url_pattern = re.compile(
r"^(https?:\/\/)?" # optional scheme
r"(([a-zA-Z\d-]+\.)+[a-zA-Z]{2,}|localhost|" # domain or localhost
r"(\d{1,3}\.){3}\d{1,3})" # or IP address
r"(:\d+)?" # optional port
r"(\/[-a-zA-Z\d%_.~+]*)*" # path
r"(\?[;&a-zA-Z\d%_.~+=-]*)?" # query string
r"(\#[-a-zA-Z\d_]*)?$", # fragment locator
re.IGNORECASE,
)
return re.match(url_pattern, value) is not None


def str_to_obj(str: str) -> Dict[str, Any]:
"""Returns a Python object from a (JSON) string."""
try:
Expand Down
2 changes: 1 addition & 1 deletion website/web/static/js/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ function drawBarChartHomePage(sightings, chartDiv, title, backgroundColor) {
// Convert the counts to an array and sort it
const sortedVulns = Object.entries(vulnerabilityCounts)
.sort(([, a], [, b]) => b - a)
.slice(0, 5);
.slice(0, 8);

const labels = sortedVulns.map(([vuln]) => vuln);
const dataCounts = sortedVulns.map(([, count]) => count);
Expand Down
15 changes: 15 additions & 0 deletions website/web/static/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ function formatMarkdownOutput() {
});
}

function truncateString(str, maxLength) {
if (str.length > maxLength) {
return str.slice(0, maxLength) + "...";
}
return str;
}

function isValidURL(string) {
try {
new URL(string);
return true;
} catch (_) {
return false;
}
}

function findCVEIdentifiers(text) {
// Regex pattern to match CVE identifiers (e.g., CVE-2021-34527)
Expand Down
4 changes: 4 additions & 0 deletions website/web/templates/admin/sightings.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ <h1>Sightings</h1>
{% for sighting in sightings %}
<tr>
<td><a href="{{ url_for('user_bp.profile', login=sighting.author.login) }}">{{ sighting.author.login }}</a></td>
{% if sighting.source | is_url %}
<td><a href="{{ sighting.source }}" rel="noreferrer" target="_blank">{{ sighting.source }}</a></td>
{% else %}
<td>{{ sighting.source }}</td>
{% endif %}
<td><a href="{{ url_for('home_bp.vulnerability_view', vulnerability_id=sighting.vulnerability) }}">{{ sighting.vulnerability.upper() }}</a></td>
<td>{{ sighting.type }}</td>
<td>{{ sighting.creation_timestamp }}</td>
Expand Down
141 changes: 139 additions & 2 deletions website/web/templates/bundles/bundle.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{% block head %}
{{ super() }}
<script src="{{ url_for('static', filename='js/pretty-print-json.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/luxon.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/utils.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/pretty-print-json.css') }}" />
{% endblock %}
Expand Down Expand Up @@ -39,7 +40,7 @@ <h2>Description</h2>
<h2>Vulnerabilities included in this bundle</h2>
<ul class="list-group">
{% for vuln in bundle.related_vulnerabilities %}
<li class="list-group-item"><a href="{{ url_for('home_bp.vulnerability_view', vulnerability_id=vuln) }}">{{ vuln }}</a></li>
<li class="list-group-item list-group-item-related" vuln-id="{{ vuln }}"><a href="{{ url_for('home_bp.vulnerability_view', vulnerability_id=vuln) }}">{{ vuln }}</a>: <span class="vuln-description"></span></li>
{% endfor %}
</ul>
{% if bundle.meta %}
Expand All @@ -48,6 +49,22 @@ <h2>Meta</h2>
{% endif %}
<h2>Author</h2>
<a href="{{ url_for('user_bp.profile', login=bundle.author.login) }}">{{ bundle.author.name }}</a>
<hr />
<h2 id="combined-sightings">Combined sightings</h2>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Author</th>
<th scope="col">Vulnerability</th>
<th scope="col">Source</th>
<th scope="col">Type</th>
<th scope="col">Date</th>
</tr>
</thead>
<tbody id="sighting-table-body"></tbody>
</table>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
jsonContainer = document.getElementById("jsoncontainer");
Expand All @@ -57,7 +74,78 @@ <h2>Author</h2>
// Post formatting
document.getElementById("markdown-description").innerHTML = linkifySecurityIdentifiers(document.getElementById("markdown-description").innerHTML);
formatMarkdownOutput();
});


// Populate the table of combined sightings.
const urlParts = window.location.pathname.split("/");
const uuid = urlParts[urlParts.length - 1];
const tableBody = document.getElementById("sighting-table-body");

// Function to create table rows
function addRowToTable(sighting) {
const row = document.createElement("tr");

const authorCell = document.createElement("td");
authorCell.textContent = sighting.author.name;
row.appendChild(authorCell);

const vulnerabilityCell = document.createElement("td");
vulnerabilityCell.innerHTML = '<a href="/vuln/'+sighting.vulnerability+'">'+sighting.vulnerability.toUpperCase()+'</a>';;
row.appendChild(vulnerabilityCell);

const sourceCell = document.createElement("td");
if (isValidURL(sighting.source)) {
sourceCell.innerHTML = '<a href="'+sighting.source+'" rel="noreferrer" target="_blank">'+sighting.source+'</a>';
} else {
sourceCell.textContent = sighting.source;
}
row.appendChild(sourceCell);

const typeCell = document.createElement("td");
typeCell.textContent = sighting.type;
row.appendChild(typeCell);

const dateCell = document.createElement("td");
dateCell.classList.add('datetime');
dateCell.textContent = sighting.creation_timestamp;
dateCell.title = sighting.creation_timestamp;
row.appendChild(dateCell);

tableBody.appendChild(row);
}

// Fetch bundle data
fetch(`/api/bundle/?uuid=${uuid}`)
.then(response => response.json())
.then(bundleData => {
const vulnerabilities = bundleData.data[0]?.related_vulnerabilities || [];

// Fetch sightings for each vulnerability
vulnerabilities.forEach(vuln_id => {
fetch(`/api/sighting/?vuln_id=${vuln_id}`)
.then(response => response.json())
.then(sightingData => {
// Sort sightings by vulnerability ID
sightingData.data.sort((a, b) => a.vulnerability.toLowerCase().localeCompare(b.vulnerability.toLowerCase()));

// Add each sighting as a row in the table
sightingData.data.forEach(sighting => addRowToTable(sighting));
})
.then(_ => {
var DateTime = luxon.DateTime;
elements = document.getElementsByClassName("datetime");
Array.prototype.forEach.call(elements, function(element) {
element.textContent = DateTime.fromISO(element.title).toRelative();

});
})
.catch(error => console.error("Error fetching sighting:", error));
});
})
.catch(error => console.error("Error fetching bundle:", error));

fetchAndAppendVulnerabilityDescriptions();
});

function copyCurrentPageURL() {
const currentURL = window.location.href; // Get the current page URL
Expand All @@ -66,6 +154,55 @@ <h2>Author</h2>
}).catch(err => {
console.error('Failed to copy: ', err);
});
}

async function fetchAndAppendVulnerabilityDescriptions() {
// Select all list items with the class `list-group-item-related`
const listItems = document.querySelectorAll('.list-group-item-related');

// Iterate through each list item
for (const listItem of listItems) {
// Get the vulnerability ID from the `vuln-id` attribute
const vulnId = listItem.getAttribute('vuln-id');

try {
// Make a GET request to fetch the vulnerability data
const response = await fetch(`/vulnerability/${vulnId}`);
if (!response.ok) throw new Error(`Failed to fetch data for ${vulnId}`);

// Parse the JSON response
const data = await response.json();

// Retrieve the title from the response (for CVE)
let description = data?.containers?.cna?.title;
// If description is not found, try to get the English description from descriptions
if (!description) {
const descriptions = data?.containers?.cna?.descriptions || [];
const englishDescription = descriptions.find(desc => desc.lang === "en");
description = englishDescription ? englishDescription.value : null;
}
// If description still not found, maybe it's GHSA security advisory
if (!description) {
description = data?.details || null;
}
// Final fallback try to find a description in a CSAF security advisory
if (!description) {
description = data?.document?.title || "No description available.";
}

if (description) {
const span = listItem.querySelector('span')
if (span) {
span.textContent = truncateString(description, 120);
} else {
console.warn(`No span found inside list item for ${vulnId}`);
}
}
} catch (error) {
console.error(`Error fetching data for ${vulnId}:`, error);
}
}
}

</script>
{% endblock %}
32 changes: 16 additions & 16 deletions website/web/templates/search.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@
<canvas id="exploitedVulnsChartSeen" height="300"></canvas>
</div>
</div>
<div class="col-md text-end">
<!-- <div class="col-md text-end">
<div id="sightingsChartContainerConfirmed" class="chart-container">
<canvas id="exploitedVulnsChartConfirmed" height="300"></canvas>
</div>
</div>
</div> -->
</div>
</div>
{% endif %}
Expand Down Expand Up @@ -319,26 +319,26 @@ <h5>All the vulnerabilites related to {{vendor}} - {{product}}</h5>
document.getElementById("sightingsChartContainerSeen").style.display = 'none';
} else {
document.getElementById("sightingsChartContainerSeen").style.display = 'block';
drawBarChartHomePage(result.data, 'exploitedVulnsChartSeen', 'Mentions dicussions, etc.', 'rgba(75, 192, 75, 0.2)');
drawBarChartHomePage(result.data, 'exploitedVulnsChartSeen', 'Mentions, dicussions, etc.', 'rgba(75, 192, 75, 0.2)');
}
})
.catch((error) => {
console.error('Error:', error);
});

fetch("{{ url_for('apiv1.sighting_sightings_list', type='confirmed') }}")
.then(response => response.json())
.then(result => {
if (result.metadata.count == 0) {
document.getElementById("sightingsChartContainerConfirmed").style.display = 'none';
} else {
document.getElementById("sightingsChartContainerConfirmed").style.display = 'block';
drawBarChartHomePage(result.data, 'exploitedVulnsChartConfirmed', 'Confirmed', 'rgba(75, 192, 192, 0.2)');
}
})
.catch((error) => {
console.error('Error:', error);
});
// fetch("{{ url_for('apiv1.sighting_sightings_list', type='confirmed') }}")
// .then(response => response.json())
// .then(result => {
// if (result.metadata.count == 0) {
// document.getElementById("sightingsChartContainerConfirmed").style.display = 'none';
// } else {
// document.getElementById("sightingsChartContainerConfirmed").style.display = 'block';
// drawBarChartHomePage(result.data, 'exploitedVulnsChartConfirmed', 'Confirmed', 'rgba(75, 192, 192, 0.2)');
// }
// })
// .catch((error) => {
// console.error('Error:', error);
// });
};

function loadComments() {
Expand Down
16 changes: 13 additions & 3 deletions website/web/templates/sightings/sightings.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ <h1>Sightings</h1>
{% for sighting in sightings %}
<tr vulnerability-id="{{ sighting.vulnerability }}">
<td><a href="{{ url_for('user_bp.profile', login=sighting.author.login) }}">{{ sighting.author.login }}</a></td>
{% if sighting.source | is_url %}
<td><a href="{{ sighting.source }}" rel="noreferrer" target="_blank">{{ sighting.source }}</a></td>
{% else %}
<td>{{ sighting.source }}</td>
{% endif %}
<td><a href="{{ url_for('home_bp.vulnerability_view', vulnerability_id=sighting.vulnerability) }}">{{ sighting.vulnerability.upper() }}</a></td>
<td>{{ sighting.type }}</td>
<td>{{ sighting.creation_timestamp | datetimeformat }}</td>
Expand All @@ -53,9 +57,15 @@ <h1>Sightings</h1>
</div>
</div>
<div class="row">
<div class="col">
{{ pagination.info }}
</div>
<div class="col">
{{ pagination.info }}
</div>
</div>
<div class="row">
<div class="col">
<br />
<p>If a line is highlighted in yellow, it indicates that the vulnerability advisory has not yet been published.</p>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
Expand Down
4 changes: 4 additions & 0 deletions website/web/templates/user/sightings.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ <h1>Your sightings</h1>
<tbody>
{% for sighting in sightings %}
<tr>
{% if sighting.source | is_url %}
<td><a href="{{ sighting.source }}" rel="noreferrer" target="_blank">{{ sighting.source }}</a></td>
{% else %}
<td>{{ sighting.source }}</td>
{% endif %}
<td><a href="{{ url_for('home_bp.vulnerability_view', vulnerability_id=sighting.vulnerability) }}">{{ sighting.vulnerability.upper() }}</a></td>
<td>{{ sighting.type }}</td>
<td>{{ sighting.creation_timestamp }}</td>
Expand Down
Loading

0 comments on commit 8d5dc2d

Please sign in to comment.