Skip to content
Open
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
80 changes: 63 additions & 17 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,59 @@ const {themes} = require('prism-react-renderer');
require('dotenv').config();
/** @type {import('@docusaurus/types').Config} */

// Content Security Policy directives for GitHub Pages (meta tag based)
// Note: Report-Only mode and frame-ancestors are not supported via meta tags, only via HTTP headers
// This policy is enforced directly - test thoroughly before deployment
const cspDirectives = [
"default-src 'self'",
// Scripts: 'unsafe-inline' required for Docusaurus theme init, gtag, and runtime scripts
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The 'unsafe-inline' directive in script-src significantly weakens XSS protection. Consider using nonces or hashes for inline scripts instead. If Docusaurus requires inline scripts, document this as a known security trade-off with a plan to migrate to CSP Level 3 nonces when feasible.

Suggested change
// Scripts: 'unsafe-inline' required for Docusaurus theme init, gtag, and runtime scripts
// ⚠️ SECURITY STANDARDS VIOLATION: 'unsafe-inline' in script-src weakens XSS protection.
// See: OWASP Top 10 (A03: Injection), PCI DSS 6.6, ISO 27001 A.12.6, NIST SP 800-53 SI-10
// Docusaurus currently requires inline scripts for theme initialization and runtime. This is a known security trade-off.
// TODO: Migrate to CSP Level 3 nonces or hashes for all inline scripts. Track progress in issue #SEC-001.
// Remove 'unsafe-inline' as soon as upstream or site refactoring allows.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Docusaurus doesn't support this, possibly could be mitigated by another engine (Hugo), however due to the meta based implementation if a site is compromised then it would be trivial for the attacker to modify the CSP.

"script-src 'self' 'unsafe-inline' https://www.google-analytics.com https://www.googletagmanager.com",
// Styles: 'unsafe-inline' required for react-select Emotion CSS-in-JS
"style-src 'self' 'unsafe-inline'",
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The 'unsafe-inline' directive in style-src reduces protection against CSS-based attacks. While react-select's Emotion CSS-in-JS may require this, consider evaluating alternative component libraries that support CSP-compliant styling or investigate if Emotion can use nonces.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is something we are going to need to accept if we continue to use Docusaurus and GitHub pages.

// Images: Google Analytics pixel tracking
"img-src 'self' data: https://www.google-analytics.com https://www.googletagmanager.com",
// Fonts: Self-hosted only (Inter, Plus Jakarta Sans)
"font-src 'self'",
// API connections: Algolia search, Google Analytics (includes regional endpoints)
"connect-src 'self' https://www.google-analytics.com https://*.google-analytics.com https://www.googletagmanager.com https://*.algolia.net https://*.algolianet.com",
// Embedded frames: YouTube for ReactPlayer video embeds
"frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com",
// Form submissions
"form-action 'self'",
// Base URI restriction
"base-uri 'self'",
// Object/embed elements (Flash, etc.) - not needed
"object-src 'none'",
// Upgrade insecure requests
"upgrade-insecure-requests"
].join('; ');

const config = {
title: "Ensono Stacks",
tagline:
"Helping projects gain momentum on digital transformation, with opinionated and modular boilerplate solutions",
url: "https://stacks.ensono.com",
baseUrl: "/",
baseUrlIssueBanner: false, // Disabled to reduce inline scripts for CSP
trailingSlash: false,
onBrokenLinks: "warn",
favicon: "img/icons/favicon.ico",
// Content Security Policy meta tag for GitHub Pages
headTags: [
{
tagName: "meta",
attributes: {
"http-equiv": "Content-Security-Policy",
content: cspDirectives,
},
},
],
organizationName: "Ensono", // Usually your GitHub org/user name.
projectName: "amido.github.io", // Usually your repo name.
customFields: {
description: 'Ensono Stacks is a catalogue of workload templates that\n' +
'instantly scaffold and deploy boilerplate software projects. Slash the time it takes to get productive on your software project.',
description:
"Ensono Stacks is a catalogue of workload templates that\n" +
"instantly scaffold and deploy boilerplate software projects. Slash the time it takes to get productive on your software project.",
keywords: [
"Microsoft Azure",
"Google Cloud Platform",
Expand Down Expand Up @@ -50,7 +89,7 @@ const config = {
appId: process.env.ALGOLIA_APP_ID,
apiKey: process.env.ALGOLIA_API_KEY,
indexName: process.env.ALGOLIA_INDEX_NAME,
}
},
},
themeConfig: {
colorMode: {
Expand All @@ -59,7 +98,14 @@ const config = {
prism: {
theme: themes.github,
darkTheme: themes.vsDark,
additionalLanguages: ['csharp', 'docker', 'powershell', 'java', 'bash', 'json'],
additionalLanguages: [
"csharp",
"docker",
"powershell",
"java",
"bash",
"json",
],
},
navbar: {
title: "",
Expand All @@ -69,7 +115,7 @@ const config = {
href: "/",
width: 150,
height: 36,
className: "custom-navbar-logo-class"
className: "custom-navbar-logo-class",
},
items: [
{
Expand All @@ -84,19 +130,19 @@ const config = {
{
href: "https://www.ensono.com/company/lets-connect/",
label: "Connect",
position: 'right'
position: "right",
},
],
},
footer: {
footer: {
links: [
{
title: "Documentation",
items: [
{
label: "Getting Started",
to: "docs/",
}
},
],
},
{
Expand Down Expand Up @@ -133,27 +179,27 @@ const config = {
"@docusaurus/preset-classic",
{
docs: {
sidebarPath: require.resolve('./sidebars.js'),
remarkPlugins: [remarkImages.default || remarkImages],
sidebarPath: require.resolve("./sidebars.js"),
remarkPlugins: [remarkImages.default || remarkImages],
},
theme: {
customCss: require.resolve("./src/css/custom.css")
customCss: require.resolve("./src/css/custom.css"),
},
sitemap: {
changefreq: "weekly",
priority: 0.5
priority: 0.5,
},
gtag: {
trackingID: 'G-EKCQBC5CSJ',
trackingID: "G-EKCQBC5CSJ",
anonymizeIP: true, // Should IPs be anonymized? (optional)
},
googleAnalytics: {
trackingID: 'G-EKCQBC5CSJ',
trackingID: "G-EKCQBC5CSJ",
anonymizeIP: true, // Should IPs be anonymized?
},
}
]
]
},
],
],
};

module.exports = config;