Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions assets/scss/_security.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* security summary page */

.security-page {
margin-top: 4rem;

@include media-breakpoint-down(sm) {
margin-top: 2rem;
}

.security-card {
border: none;
border-radius: 0.75rem;
transition: transform 0.15s ease;

&:hover {
transform: translateY(-2px);
}

.security-icon {
font-size: 1.75rem;
color: $primary;
margin-bottom: 0.5rem;
}

.security-value {
font-size: 2.5rem;
font-weight: 700;
color: $cozy-black;
line-height: 1.2;
}

.security-label {
font-size: 0.95rem;
font-weight: 600;
color: $cozy-mid-gray;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-top: 0.25rem;
}
}

.table code {
color: $primary;
font-weight: 500;
background: rgba($primary, 0.06);
padding: 0.15rem 0.4rem;
border-radius: 0.25rem;
}
}
1 change: 1 addition & 0 deletions assets/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,4 @@ a {
@import "announcement-banner";
@import "tabs_alerts";
@import "override-docsy-tabs";
@import "security";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify no `@import` appears after first non-import statement in assets/scss/main.scss
awk '
  BEGIN { seen_non_import=0; bad=0 }
  /^[[:space:]]*\/\// { next }                           # skip single-line comments
  /^[[:space:]]*$/ { next }                              # skip blanks
  /^[[:space:]]*@import[[:space:]]+/ {
    if (seen_non_import) { print "Late import at line " NR ": " $0; bad=1 }
    next
  }
  { seen_non_import=1 }
  END { exit bad }
' assets/scss/main.scss

Repository: cozystack/website

Length of output: 881


Move the @import "security" statement above non-import rules to pass SCSS linting.

The import at line 152 violates no-invalid-position-at-import-rule, which fails CI. Relocate it to the top import block with other @import statements.

Suggested fix
 // Import Docsy components
 `@import` "docsy/variables_project_after_bs";
 `@import` "docsy/support/utilities";
@@
 `@import` "docsy/support/rtl";
+@import "security";
-@import "security";
🧰 Tools
🪛 Stylelint (17.6.0)

[error] 152-152: Unexpected invalid position @import rule (no-invalid-position-at-import-rule)

(no-invalid-position-at-import-rule)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/scss/main.scss` at line 152, The `@import` "security" statement is
placed after non-import rules causing a SCSS lint failure
(no-invalid-position-at-import-rule); move the `@import` "security" line into the
top import block alongside the other `@import` statements (i.e., relocate the
`@import` "security" declaration so it appears before any non-import rules in
main.scss) to satisfy the linter.

5 changes: 5 additions & 0 deletions content/en/oss-health/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: OSS Health
description: Open source project health metrics for Cozystack
type: oss-health
---
6 changes: 6 additions & 0 deletions content/en/oss-health/security/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: Security Summary
description: Monthly public security summary for the Cozystack project
type: oss-health
layout: security
---
13 changes: 13 additions & 0 deletions data/security/monthly.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"month": "",
"generated_at": "",
"new_count": 0,
"fixed": [],
"in_progress": [],
"accepted_risk": [],
"stats": {
"total_tracked": 0,
"total_triaged": 0,
"false_positives": 0
}
}
7 changes: 7 additions & 0 deletions hugo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ params:

menus:
main:
- name: OSS Health
weight: 3
identifier: oss-health
Comment on lines +181 to +183
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The OSS Health menu item is currently defined without a URL. While it serves as a parent for the Security Summary item, it's better to link it to the overview page at /oss-health/ (which exists in the content directory) so that users can click the top-level menu item to see the section landing page.

  - name: OSS Health
    url: /oss-health/
    weight: 3
    identifier: oss-health

- name: Security Summary
parent: oss-health
url: /oss-health/security/
weight: 1
- name: Enterprise support
url: /support
weight: 5
Expand Down
18 changes: 18 additions & 0 deletions layouts/oss-health/baseof.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!doctype html>
<html lang="{{ .Site.Language.Lang }}" class="no-js">
<head>
{{ partial "head.html" . }}
</head>
<body class="td-{{ .Kind }}">
<header>
{{ partial "navbar.html" . }}
</header>
<div class="container-fluid td-outer">
<div class="td-main">
{{ block "main" . }}{{ end }}
</div>
{{ partial "footer.html" . }}
</div>
{{ partial "scripts.html" . }}
</body>
</html>
147 changes: 147 additions & 0 deletions layouts/oss-health/security.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
{{ define "main" }}
{{ $data := index .Site.Data.security "monthly" }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using the index function on .Site.Data.security is risky because it will cause a build failure if the security key is missing from .Site.Data (e.g., if the data/security/ directory does not exist). Hugo's dot notation is safer as it gracefully returns nil if any part of the path is missing.

Suggested change
{{ $data := index .Site.Data.security "monthly" }}
{{ $data := .Site.Data.security.monthly }}


<div class="security-page container py-5">
<div class="text-center mb-5">
<h1 class="display-5 fw-bold">Security Summary</h1>
<p class="lead text-muted">Monthly public security report for the Cozystack project.</p>
</div>

{{ if and $data $data.month (ne $data.month "") }}

<h3 class="text-center mb-4">{{ $data.month }}</h3>

<!-- Overview cards -->
<div class="row g-3 mb-5">
<div class="col-md-3 col-sm-6">
<div class="card text-center h-100 shadow-sm security-card">
<div class="card-body">
<div class="security-icon"><i class="fas fa-shield-alt"></i></div>
<div class="security-value">{{ $data.new_count }}</div>
<div class="security-label">New This Month</div>
</div>
</div>
</div>
<div class="col-md-3 col-sm-6">
<div class="card text-center h-100 shadow-sm security-card">
<div class="card-body">
<div class="security-icon text-success"><i class="fas fa-check-circle"></i></div>
<div class="security-value">{{ len $data.fixed }}</div>
<div class="security-label">Fixed</div>
</div>
</div>
</div>
<div class="col-md-3 col-sm-6">
<div class="card text-center h-100 shadow-sm security-card">
<div class="card-body">
<div class="security-icon text-warning"><i class="fas fa-wrench"></i></div>
<div class="security-value">{{ len $data.in_progress }}</div>
<div class="security-label">In Progress</div>
</div>
</div>
</div>
<div class="col-md-3 col-sm-6">
<div class="card text-center h-100 shadow-sm security-card">
<div class="card-body">
<div class="security-icon text-info"><i class="fas fa-info-circle"></i></div>
<div class="security-value">{{ $data.stats.total_tracked }}</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Accessing nested fields like $data.stats.total_tracked can lead to empty values in the UI if the stats object is missing or null in the JSON data. Using the default filter ensures that the card always displays a fallback value (like 0) instead of being blank.

Suggested change
<div class="security-value">{{ $data.stats.total_tracked }}</div>
<div class="security-value">{{ $data.stats.total_tracked | default 0 }}</div>

<div class="security-label">Total Tracked</div>
</div>
</div>
</div>
</div>

<!-- Fixed vulnerabilities -->
{{ if $data.fixed }}
<h5 class="mb-3"><i class="fas fa-check-circle text-success me-2"></i>Security Updates Released</h5>
<div class="table-responsive mb-5">
<table class="table table-striped table-hover">
<thead class="table-success">
<tr><th>CVE</th><th>Severity</th><th>Package</th><th>Fixed Version</th></tr>
</thead>
<tbody>
{{ range $data.fixed }}
<tr>
<td><a href="https://nvd.nist.gov/vuln/detail/{{ .cve_id }}"><code>{{ .cve_id }}</code></a></td>
<td>{{ .severity }}</td>
<td><code>{{ .package }}</code></td>
<td><code>{{ .fixed_version }}</code></td>
</tr>
{{ end }}
</tbody>
</table>
</div>
{{ end }}

<!-- In progress -->
{{ if $data.in_progress }}
<h5 class="mb-3"><i class="fas fa-wrench text-warning me-2"></i>In Progress</h5>
<div class="table-responsive mb-5">
<table class="table table-striped table-hover">
<thead class="table-warning">
<tr><th>CVE</th><th>Severity</th><th>Package</th><th>Status</th></tr>
</thead>
<tbody>
{{ range $data.in_progress }}
<tr>
<td><a href="https://nvd.nist.gov/vuln/detail/{{ .cve_id }}"><code>{{ .cve_id }}</code></a></td>
<td>{{ .severity }}</td>
<td><code>{{ .package }}</code></td>
<td>Fix in progress</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
{{ end }}

<!-- Accepted risk -->
{{ if $data.accepted_risk }}
<h5 class="mb-3"><i class="fas fa-info-circle text-info me-2"></i>Accepted Risks</h5>
<div class="table-responsive mb-5">
<table class="table table-striped table-hover">
<thead class="table-info">
<tr><th>CVE</th><th>Severity</th><th>Package</th><th>Reason</th></tr>
</thead>
<tbody>
{{ range $data.accepted_risk }}
<tr>
<td><a href="https://nvd.nist.gov/vuln/detail/{{ .cve_id }}"><code>{{ .cve_id }}</code></a></td>
<td>{{ .severity }}</td>
<td><code>{{ .package }}</code></td>
<td>{{ .reason }}</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
{{ end }}

{{ if $data.generated_at }}
<p class="text-center text-muted mt-4">
<small>Report generated: {{ $data.generated_at }}</small>
</p>
{{ end }}

{{ else }}

<div class="text-center py-5">
<div class="mb-3"><i class="fas fa-shield-alt fa-3x text-muted"></i></div>
<h4 class="text-muted">No security summary available yet</h4>
<p class="text-muted">The first monthly report will appear here after the next reporting cycle.</p>
</div>

{{ end }}

<div class="text-center mt-5 mb-3">
<p class="text-muted">
<small>
To report a vulnerability, use
<a href="https://github.com/cozystack/cozystack/security/advisories/new">GitHub Private Vulnerability Reporting</a>
or email <a href="mailto:cncf-cozystack-security@lists.cncf.io">cncf-cozystack-security@lists.cncf.io</a>.
</small>
</p>
</div>
</div>

{{ end }}