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
99 changes: 97 additions & 2 deletions apps/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,30 @@ def _process_design(
buffer = text_stream.detach() # type: ignore[assignment]
if not rows:
raise HTTPException(status_code=400, detail="Design CSV contained no rows")

overlay: OverlayConfig = app.state.overlay
strict_validation = overlay.toggles.get("strict_validation", False)

if strict_validation:
required_columns = {
"component",
"subcomponent",
"owner",
"data_class",
"description",
"control_scope",
}
missing_columns = required_columns - set(columns)
if missing_columns:
raise HTTPException(
status_code=422,
detail={
"message": "Design CSV missing required columns (strict mode)",
"missing_columns": sorted(missing_columns),
"required_columns": sorted(required_columns),
},
)

dataset = {"columns": columns, "rows": rows}
raw_bytes = _maybe_materialise_raw(buffer, total)
_store("design", dataset, original_filename=filename, raw_bytes=raw_bytes)
Expand All @@ -483,9 +507,59 @@ def _process_design(
def _process_sbom(
buffer: SpooledTemporaryFile, total: int, filename: str
) -> Dict[str, Any]:
buffer.seek(0)
try:
sbom_data = json.load(buffer)
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Nov 3, 2025

Choose a reason for hiding this comment

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

json.load(buffer) now runs for every SBOM upload, so ZIP/GZIP SBOMs trigger a JSONDecodeError and fail with HTTP 400. Please skip the JSON parsing for non-JSON inputs or branch on the content type before calling json.load.

Prompt for AI agents
Address the following comment on apps/api/app.py at line 508:

<comment>`json.load(buffer)` now runs for every SBOM upload, so ZIP/GZIP SBOMs trigger a JSONDecodeError and fail with HTTP 400. Please skip the JSON parsing for non-JSON inputs or branch on the content type before calling `json.load`.</comment>

<file context>
@@ -483,9 +503,38 @@ def _process_design(
     ) -&gt; Dict[str, Any]:
+        buffer.seek(0)
+        try:
+            sbom_data = json.load(buffer)
+        except json.JSONDecodeError as exc:
+            raise HTTPException(
</file context>
Fix with Cubic

except json.JSONDecodeError as exc:
raise HTTPException(
status_code=400, detail=f"Invalid JSON in SBOM: {exc}"
) from exc
Comment on lines 483 to +516
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject valid XML SBOM uploads by pre‑parsing as JSON

The new validation in _process_sbom now calls json.load on the upload before passing the buffer to the normalizer. InputNormalizer.load_sbom previously handled both JSON and XML CycloneDX/SPDX documents (via lib4sbom and provider fallbacks) because it works on arbitrary text. By forcing a JSON parse up front, any XML SBOMs—still valid and previously accepted—will now fail with Invalid JSON before they reach the normalizer. If the API is intended to continue supporting XML SBOMs, the bomFormat/format check needs to rely on the normalised output rather than assuming the input is JSON.

Useful? React with 👍 / 👎.


overlay: OverlayConfig = app.state.overlay
strict_validation = overlay.toggles.get("strict_validation", False)

bom_format = sbom_data.get("bomFormat")
if bom_format and bom_format not in ("CycloneDX", "SPDX"):
if strict_validation:
raise HTTPException(
status_code=422,
detail={
"message": f"Unsupported SBOM format: {bom_format}",
"supported_formats": ["CycloneDX", "SPDX"],
},
)
else:
logger.warning(
"SBOM has unsupported bomFormat: %s, continuing with provider fallback",
bom_format,
)

if not bom_format:
components = sbom_data.get("components")
detected_manifests = sbom_data.get("detectedManifests")
artifacts = sbom_data.get("artifacts")
descriptor = sbom_data.get("descriptor")

has_known_format = (
isinstance(components, list)
or isinstance(detected_manifests, dict)
or isinstance(artifacts, list)
or isinstance(descriptor, dict)
)

if not has_known_format and strict_validation:
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Nov 3, 2025

Choose a reason for hiding this comment

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

strict_validation mode is supposed to require bomFormat, but this condition only triggers when the structure is unfamiliar. A CycloneDX-style payload with components but no bomFormat will still pass in strict mode, so the bug fix never takes effect. Please raise the error whenever strict_validation is true and bomFormat is missing.

Prompt for AI agents
Address the following comment on apps/api/app.py at line 550:

<comment>`strict_validation` mode is supposed to require `bomFormat`, but this condition only triggers when the structure is unfamiliar. A CycloneDX-style payload with `components` but no `bomFormat` will still pass in strict mode, so the bug fix never takes effect. Please raise the error whenever `strict_validation` is true and `bomFormat` is missing.</comment>

<file context>
@@ -511,25 +515,46 @@ def _process_sbom(
-                    &quot;supported_formats&quot;: [&quot;CycloneDX&quot;, &quot;SPDX&quot;],
-                },
-            )
+            if not has_known_format and strict_validation:
+                raise HTTPException(
+                    status_code=422,
</file context>
Fix with Cubic

raise HTTPException(
status_code=422,
detail={
"message": "SBOM missing bomFormat and has unrecognized structure",
"hint": "Provide bomFormat field or use a known format (CycloneDX, GitHub dependency snapshot, Syft)",
},
)

buffer.seek(0)
try:
sbom: NormalizedSBOM = normalizer.load_sbom(buffer)
except Exception as exc: # pragma: no cover - pass to FastAPI
except Exception as exc:
logger.exception("SBOM normalisation failed")
raise HTTPException(
status_code=400, detail=f"Failed to parse SBOM: {exc}"
Expand All @@ -508,11 +582,27 @@ def _process_cve(
) -> Dict[str, Any]:
try:
cve_feed: NormalizedCVEFeed = normalizer.load_cve_feed(buffer)
except Exception as exc: # pragma: no cover - FastAPI serialises
except Exception as exc:
logger.exception("CVE feed normalisation failed")
raise HTTPException(
status_code=400, detail=f"Failed to parse CVE feed: {exc}"
) from exc

overlay: OverlayConfig = app.state.overlay
strict_validation = overlay.toggles.get("strict_validation", False)

if cve_feed.errors and strict_validation:
raise HTTPException(
status_code=422,
detail={
"message": "CVE feed contains validation errors (strict mode)",
"record_count": cve_feed.metadata.get("record_count", 0),
"validation_errors": cve_feed.errors[:10],
"total_errors": len(cve_feed.errors),
"hint": "Use official CVE JSON 5.1.1 format or ensure all required fields are present",
},
)

raw_bytes = _maybe_materialise_raw(buffer, total)
_store("cve", cve_feed, original_filename=filename, raw_bytes=raw_bytes)
return {
Expand Down Expand Up @@ -911,6 +1001,11 @@ async def run_pipeline() -> Dict[str, Any]:
context=app.state.artifacts.get("context"),
)
result["run_id"] = run_id

severity_overview = result.get("severity_overview", {})
guardrail_evaluation = result.get("guardrail_evaluation", {})
result["highest_severity"] = severity_overview.get("highest")
result["guardrail_status"] = guardrail_evaluation.get("status")
analytics_store = getattr(app.state, "analytics_store", None)
if analytics_store is not None:
try:
Expand Down
259 changes: 259 additions & 0 deletions e2e_orchestration/APP1/inputs/cve_feed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
{
"generated": "2024-10-28T12:00:00Z",
"source": "NVD + KEV + EPSS",
"cves": [
{
"id": "CVE-2025-0001",
"package": "pg",
"version": "8.9.0",
"cvss": 9.8,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"severity": "CRITICAL",
"published": "2024-10-01T00:00:00Z",
"summary": "Remote Code Execution via malformed SQL query in PostgreSQL Node.js driver",
"description": "A critical vulnerability in the pg (node-postgres) driver allows remote code execution through specially crafted SQL queries. The vulnerability exists in the query parsing logic and can be exploited without authentication.",
"cwe": ["CWE-94"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2025-0001",
"https://github.com/brianc/node-postgres/security/advisories/GHSA-xxxx-yyyy-zzzz"
],
"epss_score": 0.89,
"epss_percentile": 0.98,
"kev_listed": true,
"kev_date_added": "2024-10-15",
"kev_due_date": "2024-11-05",
"kev_required_action": "Apply updates per vendor instructions or discontinue use of the product if updates are unavailable.",
"kev_known_ransomware": true,
"exploit_available": true,
"exploit_maturity": "functional",
"patch_available": true,
"fixed_version": "8.11.3"
},
{
"id": "CVE-2024-1709",
"package": "express",
"version": "4.18.2",
"cvss": 7.5,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
"severity": "HIGH",
"published": "2024-05-15T00:00:00Z",
"summary": "Authentication bypass in Express.js middleware chain",
"description": "An authentication bypass vulnerability exists in Express.js when using certain middleware configurations. Attackers can bypass authentication checks by manipulating request headers.",
"cwe": ["CWE-287"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2024-1709",
"https://github.com/expressjs/express/security/advisories/GHSA-abcd-efgh-ijkl"
],
"epss_score": 0.72,
"epss_percentile": 0.94,
"kev_listed": true,
"kev_date_added": "2024-06-01",
"kev_due_date": "2024-06-22",
"kev_required_action": "Apply mitigations per vendor instructions or discontinue use of the product if mitigations are unavailable.",
"kev_known_ransomware": false,
"exploit_available": true,
"exploit_maturity": "proof-of-concept",
"patch_available": true,
"fixed_version": "4.19.2"
},
{
"id": "CVE-2024-2890",
"package": "lodash",
"version": "4.17.20",
"cvss": 7.4,
"cvss_vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N",
"severity": "HIGH",
"published": "2024-07-20T00:00:00Z",
"summary": "Prototype Pollution in lodash",
"description": "A prototype pollution vulnerability in lodash allows attackers to modify Object.prototype properties, potentially leading to remote code execution or denial of service.",
"cwe": ["CWE-1321"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2024-2890",
"https://github.com/lodash/lodash/security/advisories/GHSA-mnop-qrst-uvwx"
],
"epss_score": 0.68,
"epss_percentile": 0.91,
"kev_listed": false,
"exploit_available": true,
"exploit_maturity": "functional",
"patch_available": true,
"fixed_version": "4.17.21"
},
{
"id": "CVE-2024-5629",
"package": "axios",
"version": "1.6.0",
"cvss": 6.5,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
"severity": "MEDIUM",
"published": "2024-08-10T00:00:00Z",
"summary": "Server-Side Request Forgery (SSRF) in axios",
"description": "An SSRF vulnerability in axios allows attackers to make requests to internal network resources by manipulating the URL parameter.",
"cwe": ["CWE-918"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2024-5629",
"https://github.com/axios/axios/security/advisories/GHSA-ssrf-1234-5678"
],
"epss_score": 0.42,
"epss_percentile": 0.78,
"kev_listed": false,
"exploit_available": false,
"exploit_maturity": "unproven",
"patch_available": true,
"fixed_version": "1.6.2"
},
{
"id": "CVE-2024-7348",
"package": "jsonwebtoken",
"version": "9.0.2",
"cvss": 5.9,
"cvss_vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
"severity": "MEDIUM",
"published": "2024-09-05T00:00:00Z",
"summary": "JWT signature verification bypass",
"description": "A vulnerability in jsonwebtoken allows attackers to bypass signature verification under certain conditions, potentially leading to authentication bypass.",
"cwe": ["CWE-347"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2024-7348",
"https://github.com/auth0/node-jsonwebtoken/security/advisories/GHSA-jwt1-2345-6789"
],
"epss_score": 0.38,
"epss_percentile": 0.72,
"kev_listed": false,
"exploit_available": false,
"exploit_maturity": "unproven",
"patch_available": true,
"fixed_version": "9.0.3"
},
{
"id": "CVE-2024-8421",
"package": "redis",
"version": "4.6.10",
"cvss": 5.3,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
"severity": "MEDIUM",
"published": "2024-09-20T00:00:00Z",
"summary": "Denial of Service in Redis client",
"description": "A denial of service vulnerability in the Redis Node.js client allows attackers to cause resource exhaustion through malformed commands.",
"cwe": ["CWE-400"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2024-8421",
"https://github.com/redis/node-redis/security/advisories/GHSA-redis-dos-1234"
],
"epss_score": 0.15,
"epss_percentile": 0.52,
"kev_listed": false,
"exploit_available": false,
"exploit_maturity": "unproven",
"patch_available": true,
"fixed_version": "4.6.11"
},
{
"id": "CVE-2024-9102",
"package": "helmet",
"version": "7.1.0",
"cvss": 4.3,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N",
"severity": "MEDIUM",
"published": "2024-10-01T00:00:00Z",
"summary": "Content Security Policy bypass in helmet",
"description": "A CSP bypass vulnerability in helmet allows attackers to inject malicious scripts despite CSP headers being set.",
"cwe": ["CWE-693"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2024-9102",
"https://github.com/helmetjs/helmet/security/advisories/GHSA-csp-bypass-1234"
],
"epss_score": 0.08,
"epss_percentile": 0.35,
"kev_listed": false,
"exploit_available": false,
"exploit_maturity": "unproven",
"patch_available": true,
"fixed_version": "7.1.1"
},
{
"id": "CVE-2024-31449",
"package": "openssl",
"version": "3.1.4",
"cvss": 7.5,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"severity": "HIGH",
"published": "2024-04-08T00:00:00Z",
"summary": "Denial of Service in OpenSSL",
"description": "A vulnerability in OpenSSL's SSL/TLS implementation can cause a denial of service through excessive memory consumption.",
"cwe": ["CWE-400"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2024-31449",
"https://www.openssl.org/news/secadv/20240408.txt"
],
"epss_score": 0.55,
"epss_percentile": 0.85,
"kev_listed": false,
"exploit_available": true,
"exploit_maturity": "proof-of-concept",
"patch_available": true,
"fixed_version": "3.1.5"
},
{
"id": "CVE-2023-44487",
"package": "node",
"version": "18.18.2",
"cvss": 7.5,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"severity": "HIGH",
"published": "2023-10-10T00:00:00Z",
"summary": "HTTP/2 Rapid Reset Attack",
"description": "The HTTP/2 protocol allows a denial of service (server resource consumption) because request cancellation can reset many streams quickly.",
"cwe": ["CWE-400"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2023-44487",
"https://nodejs.org/en/blog/vulnerability/october-2023-security-releases"
],
"epss_score": 0.91,
"epss_percentile": 0.99,
"kev_listed": true,
"kev_date_added": "2023-10-10",
"kev_due_date": "2023-10-31",
"kev_required_action": "Apply mitigations per vendor instructions or discontinue use of the product if mitigations are unavailable.",
"kev_known_ransomware": false,
"exploit_available": true,
"exploit_maturity": "functional",
"patch_available": true,
"fixed_version": "18.18.3"
},
{
"id": "CVE-2024-28757",
"package": "alpine",
"version": "3.18.4",
"cvss": 6.2,
"cvss_vector": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"severity": "MEDIUM",
"published": "2024-03-15T00:00:00Z",
"summary": "Local Denial of Service in Alpine Linux",
"description": "A vulnerability in Alpine Linux's package manager allows local attackers to cause a denial of service.",
"cwe": ["CWE-400"],
"references": [
"https://nvd.nist.gov/vuln/detail/CVE-2024-28757",
"https://gitlab.alpinelinux.org/alpine/aports/-/issues/15678"
],
"epss_score": 0.04,
"epss_percentile": 0.18,
"kev_listed": false,
"exploit_available": false,
"exploit_maturity": "unproven",
"patch_available": true,
"fixed_version": "3.18.5"
}
],
"statistics": {
"total_cves": 10,
"critical": 1,
"high": 4,
"medium": 5,
"low": 0,
"kev_listed": 3,
"epss_high": 4,
"exploit_available": 6,
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Nov 3, 2025

Choose a reason for hiding this comment

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

The statistics block reports 6 CVEs with exploit_available, but only 5 records have exploit_available set to true, so the aggregate count is inconsistent with the data and will mislead any validation that relies on these totals.

Prompt for AI agents
Address the following comment on e2e_orchestration/APP1/inputs/cve_feed.json at line 256:

<comment>The statistics block reports 6 CVEs with exploit_available, but only 5 records have exploit_available set to true, so the aggregate count is inconsistent with the data and will mislead any validation that relies on these totals.</comment>

<file context>
@@ -0,0 +1,259 @@
+    &quot;low&quot;: 0,
+    &quot;kev_listed&quot;: 3,
+    &quot;epss_high&quot;: 4,
+    &quot;exploit_available&quot;: 6,
+    &quot;patch_available&quot;: 10
+  }
</file context>
Suggested change
"exploit_available": 6,
"exploit_available": 5,
Fix with Cubic

"patch_available": 10
}
}
Loading
Loading