diff --git a/website/README.md b/website/README.md index 350f2e70d..546e26eb7 100644 --- a/website/README.md +++ b/website/README.md @@ -20,6 +20,12 @@ CI blocks on critical and serious violations. Minor and moderate best-practice i Authoring conventions: resource cards use `div[role="listitem"]` wrappers, not `
`; only add `role="list"` to containers whose direct children are list items; do not nest interactive controls inside another focusable element; `.btn-primary` and ToC links must meet WCAG AA (4.5:1) contrast in both light and dark themes. +## Security hardening notes + +- The site ships with a baseline meta CSP and `referrer` policy in `src/components/Head.astro`. +- Because the site is hosted on GitHub Pages, response headers are not controllable in-repo. For stricter enforcement (for example, header-based CSP with nonce/hashes), place the site behind infrastructure that can set HTTP security headers. +- Markdown rendered for detail/file-browser experiences is sanitized with the shared `sanitizeHtml()` helper before insertion. + ## Social preview cards (LinkedIn, etc.) Shared links render as large preview cards driven by Open Graph / Twitter meta tags. diff --git a/website/src/components/Footer.astro b/website/src/components/Footer.astro index 7077d8b8d..ddfdf99fb 100644 --- a/website/src/components/Footer.astro +++ b/website/src/components/Footer.astro @@ -5,6 +5,7 @@ import Pagination from "@astrojs/starlight/components/Pagination.astro"; import config from "virtual:starlight/user-config"; import { Icon } from "@astrojs/starlight/components"; import ThemeToggle from "./ThemeToggle.astro"; +const currentYear = new Date().getFullYear(); --- @@ -81,7 +157,101 @@ import ThemeToggle from "./ThemeToggle.astro"; transition: color 0.2s ease; } - .made-by a:hover { + .subfooter a { + color: var(--sl-color-gray-3); + text-decoration: none; + } + + .made-by a:hover, + .subfooter a:hover { color: var(--sl-color-accent); + text-decoration: underline; + } + + .subfooter-bar { + width: 100%; + margin-top: 1.5rem; + background: var(--sl-color-bg-nav); + border-top: 1px solid var(--sl-color-gray-5); + } + + .subfooter { + display: flex; + align-items: center; + flex-direction: column; + gap: 1rem; + max-width: 1280px; + margin: 0 auto; + padding: 2rem 1.5rem; + color: var(--sl-color-gray-3); + text-align: left; + } + + .subfooter ul { + margin: 0; + padding: 0; + list-style: none; + } + + .subfooter-legal-nav { + color: var(--sl-color-gray-3); + font-family: var(--__sl-font-mono); + font-size: var(--sl-text-xs); + font-weight: 600; + line-height: 1.5; + } + + .subfooter-legal { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 1rem 1.25rem; + text-transform: uppercase; + } + + .subfooter-copyright { + white-space: nowrap; + } + + .social-links { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 1rem; + } + + .social-links a { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + } + + .social-links a:hover { + text-decoration: none; + } + + .social-links svg { + display: block; + width: 18px; + height: 18px; + max-width: 100%; + max-height: 100%; + fill: currentColor; + } + + @media (min-width: 768px) { + .subfooter { + flex-direction: row; + justify-content: space-between; + padding-inline: 2rem; + } + + .subfooter-legal { + justify-content: flex-start; + } } - \ No newline at end of file + diff --git a/website/src/components/Head.astro b/website/src/components/Head.astro index 6fb4a0c1f..0b84e9d13 100644 --- a/website/src/components/Head.astro +++ b/website/src/components/Head.astro @@ -34,9 +34,24 @@ const twitterDomain = canonicalUrl && URL.canParse(canonicalUrl) ? new URL(canonicalUrl).hostname : undefined; +const contentSecurityPolicy = [ + "default-src 'self'", + "base-uri 'self'", + "object-src 'none'", + "form-action 'none'", + "script-src 'self' 'unsafe-inline' https://analytics.githubassets.com", + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: https:", + "font-src 'self' data:", + "connect-src 'self' https://raw.githubusercontent.com https://collector.githubapp.com", + "worker-src 'self' blob:", + "upgrade-insecure-requests", +].join("; "); --- + + {twitterTitle && } {description && } {canonicalUrl && } @@ -77,10 +92,25 @@ const twitterDomain = name="ha-url" content="https://collector.githubapp.com/awesome-copilot-web/collect" /> - + )}