feat: add settings page with accent color toggle
- Add /settings route with SettingsLive view - Settings stored in localStorage via Settings JS hook - Accent colors (stat card borders, integrity glow, row hover) gated behind body[data-accents] CSS attribute - Add data-accent attr to stat_card component (replaces inline border classes with CSS-driven theming) - Add Settings nav item to sidebar bottom section - FOUC prevention: inline script in root layout applies saved preference before first paint
This commit is contained in:
@@ -100,6 +100,34 @@
|
||||
#sidebar .sidebar-label { display: inline !important; }
|
||||
}
|
||||
|
||||
/* Accent theming — gated behind data-accents on body */
|
||||
body[data-accents="true"] .stat-card[data-accent="critical"] {
|
||||
border-left: 2px solid var(--color-red-500);
|
||||
}
|
||||
body[data-accents="true"] .stat-card[data-accent="high"] {
|
||||
border-left: 2px solid var(--color-orange-500);
|
||||
}
|
||||
body[data-accents="true"] .stat-card[data-accent="medium"] {
|
||||
border-left: 2px solid var(--color-yellow-500);
|
||||
}
|
||||
body[data-accents="true"] .stat-card[data-accent="low"] {
|
||||
border-left: 2px solid var(--color-emerald-500);
|
||||
}
|
||||
body[data-accents="true"] .stat-card[data-accent="info"] {
|
||||
border-left: 2px solid var(--color-zinc-500);
|
||||
}
|
||||
body[data-accents="true"] .stat-card[data-accent="total"] {
|
||||
border-left: 2px solid var(--color-sky-500);
|
||||
}
|
||||
|
||||
body[data-accents="true"] .integrity-dot {
|
||||
box-shadow: 0 0 6px rgba(34, 197, 94, 0.4);
|
||||
}
|
||||
|
||||
body[data-accents="true"] .data-table tbody tr:hover {
|
||||
background: rgba(255, 255, 255, 0.015);
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
::-webkit-scrollbar-track { background: #09090b; }
|
||||
|
||||
+16
-1
@@ -54,11 +54,26 @@ const StatusBar = {
|
||||
}
|
||||
}
|
||||
|
||||
const Settings = {
|
||||
mounted() {
|
||||
const accents = localStorage.getItem("bulwark-accents") !== "false"
|
||||
this.pushEvent("settings-loaded", { accents })
|
||||
|
||||
this.handleEvent("setting-changed", ({ key, value }) => {
|
||||
if (key === "accents") {
|
||||
localStorage.setItem("bulwark-accents", String(value))
|
||||
document.body.dataset.accents = String(value)
|
||||
this.pushEvent("settings-loaded", { accents: value })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
||||
const liveSocket = new LiveSocket("/live", Socket, {
|
||||
longPollFallbackMs: 2500,
|
||||
params: {_csrf_token: csrfToken},
|
||||
hooks: {...colocatedHooks, Sidebar, StatusBar},
|
||||
hooks: {...colocatedHooks, Sidebar, StatusBar, Settings},
|
||||
})
|
||||
|
||||
topbar.config({barColors: {0: "#71717a"}, shadowColor: "rgba(0, 0, 0, .3)"})
|
||||
|
||||
@@ -472,16 +472,24 @@ defmodule SecDashboardWeb.CoreComponents do
|
||||
## Examples
|
||||
|
||||
<.stat_card label="Open Findings" value={42} />
|
||||
<.stat_card label="Critical" value={5} class="border-l-2 border-l-red-500" />
|
||||
<.stat_card label="Critical" value={5} accent="critical" />
|
||||
|
||||
"""
|
||||
attr :label, :string, required: true, doc: "the stat description"
|
||||
attr :value, :any, required: true, doc: "the stat value to display"
|
||||
|
||||
attr :accent, :string,
|
||||
default: nil,
|
||||
doc: "semantic accent color (critical, high, medium, low, info, total)"
|
||||
|
||||
attr :class, :string, default: "", doc: "additional CSS classes"
|
||||
|
||||
def stat_card(assigns) do
|
||||
~H"""
|
||||
<div class={["rounded-md border border-zinc-800 bg-zinc-900 p-4", @class]}>
|
||||
<div
|
||||
class={["stat-card rounded-md border border-zinc-800 bg-zinc-900 p-4", @class]}
|
||||
data-accent={@accent}
|
||||
>
|
||||
<p class="text-xxs font-mono font-medium uppercase tracking-wider text-zinc-500">
|
||||
{@label}
|
||||
</p>
|
||||
|
||||
@@ -30,7 +30,7 @@ defmodule SecDashboardWeb.Layouts do
|
||||
attr :active_section, :atom,
|
||||
default: nil,
|
||||
doc:
|
||||
"which sidebar nav item to highlight (:dashboard, :scans, :vulnerabilities, :findings, :assets)"
|
||||
"which sidebar nav item to highlight (:dashboard, :scans, :vulnerabilities, :findings, :assets, :settings)"
|
||||
|
||||
attr :page_title, :string,
|
||||
default: nil,
|
||||
@@ -122,6 +122,16 @@ defmodule SecDashboardWeb.Layouts do
|
||||
</nav>
|
||||
|
||||
<div class="border-t border-zinc-800 space-y-0.5 px-2 py-3">
|
||||
<.link
|
||||
navigate={~p"/settings"}
|
||||
class={nav_item_class(:settings, @active_section)}
|
||||
aria-current={if :settings == @active_section, do: "page"}
|
||||
title="Settings"
|
||||
phx-click={JS.dispatch("click", to: "#mobile-nav")}
|
||||
>
|
||||
<.icon name="hero-cog-6-tooth" class="size-4 shrink-0" />
|
||||
<span class="sidebar-label text-xs font-medium">Settings</span>
|
||||
</.link>
|
||||
<button
|
||||
data-sidebar-toggle
|
||||
class="hidden lg:flex w-full items-center gap-3 rounded-md px-2 py-2 text-zinc-500 hover:bg-zinc-900/50 hover:text-zinc-300"
|
||||
@@ -260,11 +270,13 @@ defmodule SecDashboardWeb.Layouts do
|
||||
defp section_label(:vulnerabilities), do: "Vulnerabilities"
|
||||
defp section_label(:findings), do: "Findings"
|
||||
defp section_label(:assets), do: "Assets"
|
||||
defp section_label(:settings), do: "Settings"
|
||||
defp section_label(_), do: nil
|
||||
|
||||
defp section_path("Scans"), do: ~p"/scans"
|
||||
defp section_path("Vulnerabilities"), do: ~p"/vulnerabilities"
|
||||
defp section_path("Findings"), do: ~p"/findings"
|
||||
defp section_path("Assets"), do: ~p"/assets"
|
||||
defp section_path("Settings"), do: ~p"/settings"
|
||||
defp section_path(_), do: ~p"/"
|
||||
end
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
</script>
|
||||
</head>
|
||||
<body class="h-full bg-zinc-950 text-zinc-50">
|
||||
<script>
|
||||
(function() {
|
||||
var a = localStorage.getItem("bulwark-accents");
|
||||
document.body.dataset.accents = a === null ? "true" : a;
|
||||
})();
|
||||
</script>
|
||||
{@inner_content}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -65,30 +65,26 @@ defmodule SecDashboardWeb.DashboardLive do
|
||||
</header>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3 md:grid-cols-5">
|
||||
<.stat_card
|
||||
label="Open Findings"
|
||||
value={@open_findings}
|
||||
class="border-l-2 border-l-sky-500"
|
||||
/>
|
||||
<.stat_card label="Open Findings" value={@open_findings} accent="total" />
|
||||
<.stat_card
|
||||
label="Critical"
|
||||
value={Map.get(@severity_counts, "critical", 0)}
|
||||
class="border-l-2 border-l-red-500"
|
||||
accent="critical"
|
||||
/>
|
||||
<.stat_card
|
||||
label="High"
|
||||
value={Map.get(@severity_counts, "high", 0)}
|
||||
class="border-l-2 border-l-orange-500"
|
||||
accent="high"
|
||||
/>
|
||||
<.stat_card
|
||||
label="Medium"
|
||||
value={Map.get(@severity_counts, "medium", 0)}
|
||||
class="border-l-2 border-l-yellow-500"
|
||||
accent="medium"
|
||||
/>
|
||||
<.stat_card
|
||||
label="Low"
|
||||
value={Map.get(@severity_counts, "low", 0)}
|
||||
class="border-l-2 border-l-emerald-500"
|
||||
accent="low"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
defmodule SecDashboardWeb.SettingsLive do
|
||||
@moduledoc """
|
||||
LiveView for application display settings at `/settings`.
|
||||
|
||||
Settings are stored client-side in localStorage via the `Settings` JS hook.
|
||||
The hook pushes current values to the server on mount so toggles render
|
||||
correctly, and handles persistence when the user changes a setting.
|
||||
"""
|
||||
use SecDashboardWeb, :live_view
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
socket =
|
||||
socket
|
||||
|> assign(:page_title, "Settings")
|
||||
|> assign(:active_section, :settings)
|
||||
|> assign(:accents, true)
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("settings-loaded", %{"accents" => accents}, socket) do
|
||||
{:noreply, assign(socket, :accents, accents)}
|
||||
end
|
||||
|
||||
def handle_event("toggle-accents", _params, socket) do
|
||||
new_value = !socket.assigns.accents
|
||||
{:noreply, push_event(socket, "setting-changed", %{key: "accents", value: new_value})}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<Layouts.app flash={@flash} active_section={@active_section}>
|
||||
<div id="settings" phx-hook="Settings" class="mx-auto max-w-3xl space-y-6">
|
||||
<header>
|
||||
<h1 class="text-2xl font-bold tracking-tight text-zinc-100">Settings</h1>
|
||||
<p class="mt-1 text-sm text-zinc-400">
|
||||
Configure display preferences for the dashboard
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<section class="rounded-md border border-zinc-800 bg-zinc-900">
|
||||
<div class="border-b border-zinc-800 px-5 py-3">
|
||||
<h2 class="text-sm font-semibold text-zinc-100">Display</h2>
|
||||
</div>
|
||||
|
||||
<div class="divide-y divide-zinc-800">
|
||||
<div class="flex items-center justify-between px-5 py-4">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-zinc-200">Accent colors</p>
|
||||
<p class="mt-0.5 text-xs text-zinc-500">
|
||||
Show colored borders on stat cards, glow effects, and row highlights
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
phx-click="toggle-accents"
|
||||
role="switch"
|
||||
aria-checked={to_string(@accents)}
|
||||
class={[
|
||||
"relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border border-zinc-700 transition-colors",
|
||||
if(@accents, do: "bg-sky-600 border-sky-600", else: "bg-zinc-800")
|
||||
]}
|
||||
>
|
||||
<span class={[
|
||||
"pointer-events-none inline-block size-3.5 translate-y-px rounded-full bg-white shadow transition-transform",
|
||||
if(@accents, do: "translate-x-4", else: "translate-x-0.5")
|
||||
]} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<p class="text-xs text-zinc-600">
|
||||
Settings are stored locally in your browser.
|
||||
</p>
|
||||
</div>
|
||||
</Layouts.app>
|
||||
"""
|
||||
end
|
||||
end
|
||||
@@ -25,6 +25,7 @@ defmodule SecDashboardWeb.Router do
|
||||
live "/findings", FindingLive.Index
|
||||
live "/assets", AssetLive.Index
|
||||
live "/assets/:id", AssetLive.Show
|
||||
live "/settings", SettingsLive
|
||||
end
|
||||
|
||||
# Other scopes may use custom stacks.
|
||||
|
||||
+64
-31
@@ -1,21 +1,54 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
||||
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
|
||||
"version": "2.1.0",
|
||||
"runs": [
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "checkov",
|
||||
"version": "3.1.40",
|
||||
"semanticVersion": "3.1.40",
|
||||
"informationUri": "https://www.checkov.io",
|
||||
"rules": [
|
||||
{ "id": "CKV_AWS_18", "shortDescription": { "text": "Ensure the S3 bucket has access logging enabled" }, "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-13-enable-logging" },
|
||||
{ "id": "CKV_AWS_145", "shortDescription": { "text": "Ensure that S3 buckets are encrypted with KMS by default" }, "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-s3-buckets-are-encrypted-with-kms-by-default" },
|
||||
{ "id": "CKV_AWS_21", "shortDescription": { "text": "Ensure the S3 bucket has versioning enabled" }, "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-16-enable-versioning" },
|
||||
{ "id": "CKV_AWS_23", "shortDescription": { "text": "Ensure every security group and rule has a description" }, "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/networking-31" },
|
||||
{ "id": "CKV_AWS_79", "shortDescription": { "text": "Ensure Instance Metadata Service Version 1 is not enabled" }, "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/bc-aws-general-31" },
|
||||
{ "id": "CKV_AWS_88", "shortDescription": { "text": "EC2 instance should not have public IP" }, "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/ensure-aws-ec2-instance-does-not-have-a-public-ip" },
|
||||
{ "id": "CKV_AWS_260", "shortDescription": { "text": "Ensure no security group allows ingress from 0.0.0.0/0 to port 80" }, "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/ensure-aws-security-group-does-not-allow-ingress-from-0000-to-port-80" },
|
||||
{ "id": "CKV2_AWS_5", "shortDescription": { "text": "Ensure that Security Groups are attached to an ENI or EC2 instance" }, "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/ensure-that-security-groups-are-attached-to-ec2-instances-or-elastic-network-interfaces-enis" }
|
||||
{
|
||||
"id": "CKV_AWS_18",
|
||||
"shortDescription": { "text": "Ensure the S3 bucket has access logging enabled" },
|
||||
"helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-13-enable-logging"
|
||||
},
|
||||
{
|
||||
"id": "CKV_AWS_145",
|
||||
"shortDescription": { "text": "Ensure that S3 buckets are encrypted with KMS by default" },
|
||||
"helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/ensure-that-s3-buckets-are-encrypted-with-kms-by-default"
|
||||
},
|
||||
{
|
||||
"id": "CKV_AWS_21",
|
||||
"shortDescription": { "text": "Ensure the S3 bucket has versioning enabled" },
|
||||
"helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/s3-policies/s3-16-enable-versioning"
|
||||
},
|
||||
{
|
||||
"id": "CKV_AWS_23",
|
||||
"shortDescription": { "text": "Ensure every security groups rule has a description" },
|
||||
"helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/networking-31"
|
||||
},
|
||||
{
|
||||
"id": "CKV_AWS_24",
|
||||
"shortDescription": { "text": "Ensure no security group allows ingress from 0.0.0.0/0 to port 22" },
|
||||
"helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/networking-1-port-security"
|
||||
},
|
||||
{
|
||||
"id": "CKV_AWS_79",
|
||||
"shortDescription": { "text": "Ensure Instance Metadata Service Version 1 is not enabled" },
|
||||
"helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-general-policies/bc-aws-general-31"
|
||||
},
|
||||
{
|
||||
"id": "CKV_AWS_88",
|
||||
"shortDescription": { "text": "EC2 instance should not have public IP" },
|
||||
"helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/bc-aws-general-37"
|
||||
},
|
||||
{
|
||||
"id": "CKV_AWS_260",
|
||||
"shortDescription": { "text": "Ensure no security group allows ingress from 0.0.0.0/0 to port 80" },
|
||||
"helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/aws-policies/aws-networking-policies/ensure-aws-security-group-does-not-allow-ingress-from-0-0-0-0-0-to-port-80"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -23,50 +56,50 @@
|
||||
{
|
||||
"ruleId": "CKV_AWS_18",
|
||||
"level": "warning",
|
||||
"message": { "text": "S3 bucket 'data-lake-raw' does not have access logging enabled" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/storage/main.tf" }, "region": { "startLine": 12 } } }]
|
||||
"message": { "text": "S3 bucket 'data-lake-raw' does not have access logging enabled. Enable access logging to track requests for compliance and security auditing." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/storage/s3.tf" }, "region": { "startLine": 12, "startColumn": 1, "endLine": 24, "endColumn": 1 } } }]
|
||||
},
|
||||
{
|
||||
"ruleId": "CKV_AWS_145",
|
||||
"level": "warning",
|
||||
"message": { "text": "S3 bucket 'data-lake-raw' is not encrypted with KMS" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/storage/main.tf" }, "region": { "startLine": 12 } } }]
|
||||
"message": { "text": "S3 bucket 'data-lake-raw' is not encrypted with a KMS customer managed key. Use aws_kms_key for server-side encryption." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/storage/s3.tf" }, "region": { "startLine": 12, "startColumn": 1, "endLine": 24, "endColumn": 1 } } }]
|
||||
},
|
||||
{
|
||||
"ruleId": "CKV_AWS_21",
|
||||
"level": "warning",
|
||||
"message": { "text": "S3 bucket 'data-lake-raw' does not have versioning enabled" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/storage/main.tf" }, "region": { "startLine": 12 } } }]
|
||||
"message": { "text": "S3 bucket 'artifacts-prod' does not have versioning enabled. Enable versioning to preserve, retrieve, and restore every version of every object." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/storage/s3.tf" }, "region": { "startLine": 42, "startColumn": 1, "endLine": 56, "endColumn": 1 } } }]
|
||||
},
|
||||
{
|
||||
"ruleId": "CKV_AWS_23",
|
||||
"level": "note",
|
||||
"message": { "text": "Security group 'api-sg' does not have a description" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/networking/security_groups.tf" }, "region": { "startLine": 1 } } }]
|
||||
"message": { "text": "Security group 'web-sg' rule does not have a description. Add descriptions to all rules for auditability." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/network/security_groups.tf" }, "region": { "startLine": 8, "startColumn": 1, "endLine": 22, "endColumn": 1 } } }]
|
||||
},
|
||||
{
|
||||
"ruleId": "CKV_AWS_24",
|
||||
"level": "error",
|
||||
"message": { "text": "Security group 'web-sg' allows SSH access from 0.0.0.0/0. Restrict SSH access to known IP ranges." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/network/security_groups.tf" }, "region": { "startLine": 15, "startColumn": 3, "endLine": 20, "endColumn": 3 } } }]
|
||||
},
|
||||
{
|
||||
"ruleId": "CKV_AWS_79",
|
||||
"level": "error",
|
||||
"message": { "text": "EC2 instance 'worker-node' has IMDSv1 enabled" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/compute/main.tf" }, "region": { "startLine": 45 } } }]
|
||||
"message": { "text": "EC2 instance 'api-server' has IMDSv1 enabled. Enforce IMDSv2 to prevent SSRF-based credential theft." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/compute/ec2.tf" }, "region": { "startLine": 1, "startColumn": 1, "endLine": 32, "endColumn": 1 } } }]
|
||||
},
|
||||
{
|
||||
"ruleId": "CKV_AWS_88",
|
||||
"level": "error",
|
||||
"message": { "text": "EC2 instance 'bastion' has a public IP address assigned" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/compute/bastion.tf" }, "region": { "startLine": 8 } } }]
|
||||
"message": { "text": "EC2 instance 'api-server' has associate_public_ip_address set to true. Use a NAT gateway or load balancer instead of a public IP." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/compute/ec2.tf" }, "region": { "startLine": 14, "startColumn": 3, "endLine": 14, "endColumn": 42 } } }]
|
||||
},
|
||||
{
|
||||
"ruleId": "CKV_AWS_260",
|
||||
"level": "error",
|
||||
"message": { "text": "Security group 'api-sg' allows ingress from 0.0.0.0/0 to port 80" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/networking/security_groups.tf" }, "region": { "startLine": 15 } } }]
|
||||
},
|
||||
{
|
||||
"ruleId": "CKV2_AWS_5",
|
||||
"level": "note",
|
||||
"message": { "text": "Security group 'legacy-db-sg' is not attached to any ENI or EC2 instance" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/networking/security_groups.tf" }, "region": { "startLine": 42 } } }]
|
||||
"level": "warning",
|
||||
"message": { "text": "Security group 'alb-sg' allows ingress from 0.0.0.0/0 to port 80. Consider restricting to HTTPS only (port 443) with a redirect." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "modules/network/security_groups.tf" }, "region": { "startLine": 34, "startColumn": 3, "endLine": 40, "endColumn": 3 } } }]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+43
-17
@@ -1,18 +1,39 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/sarif-2.1.0-rtm.5.json",
|
||||
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
|
||||
"version": "2.1.0",
|
||||
"runs": [
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "gitleaks",
|
||||
"version": "8.18.1",
|
||||
"semanticVersion": "8.18.1",
|
||||
"informationUri": "https://github.com/gitleaks/gitleaks",
|
||||
"rules": [
|
||||
{ "id": "generic-api-key", "shortDescription": { "text": "Generic API Key" }, "helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/generic.go" },
|
||||
{ "id": "aws-access-key-id", "shortDescription": { "text": "AWS Access Key ID" }, "helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/aws.go" },
|
||||
{ "id": "private-key", "shortDescription": { "text": "Private Key" }, "helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/privatekey.go" },
|
||||
{ "id": "github-pat", "shortDescription": { "text": "GitHub Personal Access Token" }, "helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/github.go" },
|
||||
{ "id": "slack-webhook-url", "shortDescription": { "text": "Slack Webhook URL" }, "helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/slack.go" }
|
||||
{
|
||||
"id": "generic-api-key",
|
||||
"shortDescription": { "text": "Generic API Key" },
|
||||
"helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/generic.go"
|
||||
},
|
||||
{
|
||||
"id": "aws-access-key-id",
|
||||
"shortDescription": { "text": "AWS Access Key ID" },
|
||||
"helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/aws.go"
|
||||
},
|
||||
{
|
||||
"id": "private-key",
|
||||
"shortDescription": { "text": "Private Key" },
|
||||
"helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/privatekey.go"
|
||||
},
|
||||
{
|
||||
"id": "github-pat",
|
||||
"shortDescription": { "text": "GitHub Personal Access Token" },
|
||||
"helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/github.go"
|
||||
},
|
||||
{
|
||||
"id": "slack-webhook-url",
|
||||
"shortDescription": { "text": "Slack Webhook URL" },
|
||||
"helpUri": "https://github.com/gitleaks/gitleaks/blob/master/cmd/generate/config/rules/slack.go"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -20,32 +41,37 @@
|
||||
{
|
||||
"ruleId": "generic-api-key",
|
||||
"level": "error",
|
||||
"message": { "text": "Generic API Key detected in environment file" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": ".env" }, "region": { "startLine": 3, "startColumn": 16, "endColumn": 48 } } }]
|
||||
"message": { "text": "Generic API Key detected: matched pattern for SendGrid API key in environment configuration file." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": ".env" }, "region": { "startLine": 3, "startColumn": 16, "endLine": 3, "endColumn": 85, "snippet": { "text": "SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } } }],
|
||||
"partialFingerprints": { "primaryLocationLineHash": "d4e5f6a7b8c9d0e1" }
|
||||
},
|
||||
{
|
||||
"ruleId": "aws-access-key-id",
|
||||
"level": "error",
|
||||
"message": { "text": "AWS Access Key ID found in configuration" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "config/deploy.py" }, "region": { "startLine": 14, "startColumn": 22, "endColumn": 42 } } }]
|
||||
"message": { "text": "AWS Access Key ID detected in Python deployment script. Rotate this key immediately and use IAM roles or environment variables." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "scripts/deploy.py" }, "region": { "startLine": 14, "startColumn": 22, "endLine": 14, "endColumn": 42, "snippet": { "text": "aws_access_key_id = \"AKIAIOSFODNN7EXAMPLE\"" } } } }],
|
||||
"partialFingerprints": { "primaryLocationLineHash": "a1b2c3d4e5f6a7b8" }
|
||||
},
|
||||
{
|
||||
"ruleId": "private-key",
|
||||
"level": "error",
|
||||
"message": { "text": "Private key detected in source tree" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "certs/server.key" }, "region": { "startLine": 1, "startColumn": 1, "endColumn": 32 } } }]
|
||||
"message": { "text": "RSA private key detected embedded directly in JavaScript source file. Move to a secrets manager or encrypted storage." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "src/auth/keys.js" }, "region": { "startLine": 8, "startColumn": 1, "endLine": 28, "endColumn": 1, "snippet": { "text": "const PRIVATE_KEY = `-----BEGIN RSA PRIVATE KEY-----" } } } }],
|
||||
"partialFingerprints": { "primaryLocationLineHash": "c3d4e5f6a7b8c9d0" }
|
||||
},
|
||||
{
|
||||
"ruleId": "github-pat",
|
||||
"level": "error",
|
||||
"message": { "text": "GitHub Personal Access Token found in CI config" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": ".github/workflows/deploy.yml" }, "region": { "startLine": 28, "startColumn": 18, "endColumn": 58 } } }]
|
||||
"message": { "text": "GitHub Personal Access Token found hardcoded in CI/CD workflow configuration. Use GitHub Actions secrets instead." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": ".github/workflows/deploy.yml" }, "region": { "startLine": 22, "startColumn": 18, "endLine": 22, "endColumn": 58, "snippet": { "text": "token: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } } }],
|
||||
"partialFingerprints": { "primaryLocationLineHash": "e5f6a7b8c9d0e1f2" }
|
||||
},
|
||||
{
|
||||
"ruleId": "slack-webhook-url",
|
||||
"level": "warning",
|
||||
"message": { "text": "Slack Webhook URL detected in notification handler" },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "scripts/notify.js" }, "region": { "startLine": 7, "startColumn": 24, "endColumn": 96 } } }]
|
||||
"message": { "text": "Slack Webhook URL detected in notification service configuration. Move to environment variable to prevent unauthorized message posting." },
|
||||
"locations": [{ "physicalLocation": { "artifactLocation": { "uri": "src/notifications/slack.py" }, "region": { "startLine": 5, "startColumn": 15, "endLine": 5, "endColumn": 89, "snippet": { "text": "WEBHOOK_URL = \"https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX\"" } } } }],
|
||||
"partialFingerprints": { "primaryLocationLineHash": "f6a7b8c9d0e1f2a3" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+212
-88
@@ -3,125 +3,249 @@
|
||||
"dataLicense": "CC0-1.0",
|
||||
"SPDXID": "SPDXRef-DOCUMENT",
|
||||
"name": "api-server-sbom",
|
||||
"documentNamespace": "https://spdx.org/spdxdocs/api-server-sbom-v2.4.1",
|
||||
"documentNamespace": "https://spdx.org/spdxdocs/api-server-sbom-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"creationInfo": {
|
||||
"created": "2026-05-10T08:30:00Z",
|
||||
"creators": ["Tool: syft-0.105.0", "Organization: myorg"]
|
||||
"created": "2024-11-15T14:22:00Z",
|
||||
"creators": [
|
||||
"Tool: syft-0.105.0",
|
||||
"Organization: myorg"
|
||||
],
|
||||
"licenseListVersion": "3.22"
|
||||
},
|
||||
"packages": [
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-flask",
|
||||
"name": "api-server",
|
||||
"SPDXID": "SPDXRef-Package-api-server",
|
||||
"versionInfo": "2.4.1",
|
||||
"downloadLocation": "https://github.com/myorg/api-server",
|
||||
"primaryPackagePurpose": "APPLICATION",
|
||||
"supplier": "Organization: myorg",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/api-server@2.4.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "flask",
|
||||
"SPDXID": "SPDXRef-Package-flask",
|
||||
"versionInfo": "3.0.0",
|
||||
"downloadLocation": "https://pypi.org/project/Flask/3.0.0/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/flask@3.0.0" }]
|
||||
"supplier": "Organization: Pallets",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/flask@3.0.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-werkzeug",
|
||||
"name": "werkzeug",
|
||||
"SPDXID": "SPDXRef-Package-werkzeug",
|
||||
"versionInfo": "3.0.1",
|
||||
"downloadLocation": "https://pypi.org/project/Werkzeug/3.0.1/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/werkzeug@3.0.1" }]
|
||||
"supplier": "Organization: Pallets",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/werkzeug@3.0.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-jinja2",
|
||||
"name": "jinja2",
|
||||
"SPDXID": "SPDXRef-Package-jinja2",
|
||||
"versionInfo": "3.1.2",
|
||||
"downloadLocation": "https://pypi.org/project/Jinja2/3.1.2/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/jinja2@3.1.2" }]
|
||||
"supplier": "Organization: Pallets",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/jinja2@3.1.2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-requests",
|
||||
"name": "requests",
|
||||
"versionInfo": "2.31.0",
|
||||
"downloadLocation": "https://pypi.org/project/requests/2.31.0/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/requests@2.31.0" }]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-urllib3",
|
||||
"name": "urllib3",
|
||||
"versionInfo": "2.1.0",
|
||||
"downloadLocation": "https://pypi.org/project/urllib3/2.1.0/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/urllib3@2.1.0" }]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-sqlalchemy",
|
||||
"name": "sqlalchemy",
|
||||
"versionInfo": "2.0.23",
|
||||
"downloadLocation": "https://pypi.org/project/SQLAlchemy/2.0.23/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/sqlalchemy@2.0.23" }]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-pydantic",
|
||||
"name": "pydantic",
|
||||
"versionInfo": "2.5.2",
|
||||
"downloadLocation": "https://pypi.org/project/pydantic/2.5.2/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/pydantic@2.5.2" }]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-celery",
|
||||
"name": "celery",
|
||||
"versionInfo": "5.3.6",
|
||||
"downloadLocation": "https://pypi.org/project/celery/5.3.6/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/celery@5.3.6" }]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-redis",
|
||||
"name": "redis",
|
||||
"versionInfo": "5.0.1",
|
||||
"downloadLocation": "https://pypi.org/project/redis/5.0.1/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/redis@5.0.1" }]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-cryptography",
|
||||
"name": "cryptography",
|
||||
"versionInfo": "41.0.7",
|
||||
"downloadLocation": "https://pypi.org/project/cryptography/41.0.7/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/cryptography@41.0.7" }]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-gunicorn",
|
||||
"name": "gunicorn",
|
||||
"versionInfo": "21.2.0",
|
||||
"downloadLocation": "https://pypi.org/project/gunicorn/21.2.0/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/gunicorn@21.2.0" }]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-psycopg2",
|
||||
"name": "psycopg2-binary",
|
||||
"versionInfo": "2.9.9",
|
||||
"downloadLocation": "https://pypi.org/project/psycopg2-binary/2.9.9/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/psycopg2-binary@2.9.9" }]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-markupsafe",
|
||||
"name": "markupsafe",
|
||||
"SPDXID": "SPDXRef-Package-markupsafe",
|
||||
"versionInfo": "2.1.3",
|
||||
"downloadLocation": "https://pypi.org/project/MarkupSafe/2.1.3/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/markupsafe@2.1.3" }]
|
||||
"supplier": "Organization: Pallets",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/markupsafe@2.1.3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-click",
|
||||
"name": "click",
|
||||
"versionInfo": "8.1.7",
|
||||
"downloadLocation": "https://pypi.org/project/click/8.1.7/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/click@8.1.7" }]
|
||||
"name": "requests",
|
||||
"SPDXID": "SPDXRef-Package-requests",
|
||||
"versionInfo": "2.31.0",
|
||||
"downloadLocation": "https://pypi.org/project/requests/2.31.0/",
|
||||
"supplier": "Organization: Python Software Foundation",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/requests@2.31.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "urllib3",
|
||||
"SPDXID": "SPDXRef-Package-urllib3",
|
||||
"versionInfo": "2.1.0",
|
||||
"downloadLocation": "https://pypi.org/project/urllib3/2.1.0/",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/urllib3@2.1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-Package-certifi",
|
||||
"name": "certifi",
|
||||
"SPDXID": "SPDXRef-Package-certifi",
|
||||
"versionInfo": "2023.11.17",
|
||||
"downloadLocation": "https://pypi.org/project/certifi/2023.11.17/",
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/certifi@2023.11.17" }]
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/certifi@2023.11.17"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idna",
|
||||
"SPDXID": "SPDXRef-Package-idna",
|
||||
"versionInfo": "3.6",
|
||||
"downloadLocation": "https://pypi.org/project/idna/3.6/",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/idna@3.6"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sqlalchemy",
|
||||
"SPDXID": "SPDXRef-Package-sqlalchemy",
|
||||
"versionInfo": "2.0.23",
|
||||
"downloadLocation": "https://pypi.org/project/SQLAlchemy/2.0.23/",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/sqlalchemy@2.0.23"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pyjwt",
|
||||
"SPDXID": "SPDXRef-Package-pyjwt",
|
||||
"versionInfo": "2.8.0",
|
||||
"downloadLocation": "https://pypi.org/project/PyJWT/2.8.0/",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/pyjwt@2.8.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "gunicorn",
|
||||
"SPDXID": "SPDXRef-Package-gunicorn",
|
||||
"versionInfo": "21.2.0",
|
||||
"downloadLocation": "https://pypi.org/project/gunicorn/21.2.0/",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/gunicorn@21.2.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "celery",
|
||||
"SPDXID": "SPDXRef-Package-celery",
|
||||
"versionInfo": "5.3.6",
|
||||
"downloadLocation": "https://pypi.org/project/celery/5.3.6/",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/celery@5.3.6"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "redis",
|
||||
"SPDXID": "SPDXRef-Package-redis",
|
||||
"versionInfo": "5.0.1",
|
||||
"downloadLocation": "https://pypi.org/project/redis/5.0.1/",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/redis@5.0.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "cryptography",
|
||||
"SPDXID": "SPDXRef-Package-cryptography",
|
||||
"versionInfo": "41.0.7",
|
||||
"downloadLocation": "https://pypi.org/project/cryptography/41.0.7/",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [
|
||||
{
|
||||
"referenceCategory": "PACKAGE-MANAGER",
|
||||
"referenceType": "purl",
|
||||
"referenceLocator": "pkg:pypi/cryptography@41.0.7"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{ "spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-Package-flask", "relationshipType": "DESCRIBES" },
|
||||
{ "spdxElementId": "SPDXRef-Package-flask", "relatedSpdxElement": "SPDXRef-Package-werkzeug", "relationshipType": "DEPENDS_ON" },
|
||||
{ "spdxElementId": "SPDXRef-Package-flask", "relatedSpdxElement": "SPDXRef-Package-jinja2", "relationshipType": "DEPENDS_ON" },
|
||||
{ "spdxElementId": "SPDXRef-Package-flask", "relatedSpdxElement": "SPDXRef-Package-click", "relationshipType": "DEPENDS_ON" },
|
||||
{ "spdxElementId": "SPDXRef-Package-flask", "relatedSpdxElement": "SPDXRef-Package-markupsafe", "relationshipType": "DEPENDS_ON" },
|
||||
{ "spdxElementId": "SPDXRef-Package-requests", "relatedSpdxElement": "SPDXRef-Package-urllib3", "relationshipType": "DEPENDS_ON" },
|
||||
{ "spdxElementId": "SPDXRef-Package-requests", "relatedSpdxElement": "SPDXRef-Package-certifi", "relationshipType": "DEPENDS_ON" }
|
||||
{ "spdxElementId": "SPDXRef-DOCUMENT", "relationshipType": "DESCRIBES", "relatedSpdxElement": "SPDXRef-Package-api-server" },
|
||||
{ "spdxElementId": "SPDXRef-Package-api-server", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-flask" },
|
||||
{ "spdxElementId": "SPDXRef-Package-api-server", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-requests" },
|
||||
{ "spdxElementId": "SPDXRef-Package-api-server", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-sqlalchemy" },
|
||||
{ "spdxElementId": "SPDXRef-Package-api-server", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-pyjwt" },
|
||||
{ "spdxElementId": "SPDXRef-Package-api-server", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-gunicorn" },
|
||||
{ "spdxElementId": "SPDXRef-Package-api-server", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-celery" },
|
||||
{ "spdxElementId": "SPDXRef-Package-api-server", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-redis" },
|
||||
{ "spdxElementId": "SPDXRef-Package-api-server", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-cryptography" },
|
||||
{ "spdxElementId": "SPDXRef-Package-flask", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-werkzeug" },
|
||||
{ "spdxElementId": "SPDXRef-Package-flask", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-jinja2" },
|
||||
{ "spdxElementId": "SPDXRef-Package-jinja2", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-markupsafe" },
|
||||
{ "spdxElementId": "SPDXRef-Package-requests", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-urllib3" },
|
||||
{ "spdxElementId": "SPDXRef-Package-requests", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-certifi" },
|
||||
{ "spdxElementId": "SPDXRef-Package-requests", "relationshipType": "DEPENDS_ON", "relatedSpdxElement": "SPDXRef-Package-idna" }
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user