From 5a13a7d5da625a6d44a06e8a1cc7f95fc569ad7e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 02:36:57 +0000 Subject: [PATCH 1/7] feat(website): add charset header, integrity attributes, and disable directory indexing - Create Astro middleware to set Content-Type charset=utf-8 for all HTML responses - Add integrity attribute (SHA-384) to 1ds-init.js script in base-layout and Starlight config - Add crossorigin="anonymous" to external wcp-consent.js script - Disable directory indexing for /docs/handbook/configuration/configuration/ via meta robots tag and middleware X-Robots-Tag header Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/7dfd7541-76dc-4f3c-9397-bdedcfaca7a0 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- website/astro.config.mjs | 2 ++ .../handbook/configuration/configuration.mdx | 5 +++++ website/src/layouts/base-layout.astro | 11 +++++++--- website/src/middleware.ts | 21 +++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 website/src/middleware.ts diff --git a/website/astro.config.mjs b/website/astro.config.mjs index b495713134e..84390e5b504 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -98,6 +98,7 @@ export default defineConfig({ tag: "script", attrs: { src: "https://consentdeliveryfd.azurefd.net/mscc/lib/v2/wcp-consent.js", + crossorigin: "anonymous", }, }, { @@ -106,6 +107,7 @@ export default defineConfig({ type: "module", async: true, src: "1ds-init.js", + integrity: "sha384-idYZGvEvTNaaqBGjIeT/diHjEM13+k+Utpb4k+TjTW/ui8yYALU1FXvgk+lG/EZG", }, }, ], diff --git a/website/src/content/docs/docs/handbook/configuration/configuration.mdx b/website/src/content/docs/docs/handbook/configuration/configuration.mdx index 2c53ec7ec14..68a491bc92d 100644 --- a/website/src/content/docs/docs/handbook/configuration/configuration.mdx +++ b/website/src/content/docs/docs/handbook/configuration/configuration.mdx @@ -1,5 +1,10 @@ --- title: Configuration +head: + - tag: meta + attrs: + name: robots + content: noindex --- import { FileTree } from "@astrojs/starlight/components"; diff --git a/website/src/layouts/base-layout.astro b/website/src/layouts/base-layout.astro index 4d2361eab40..8aef70ced5d 100644 --- a/website/src/layouts/base-layout.astro +++ b/website/src/layouts/base-layout.astro @@ -18,9 +18,14 @@ const { footer = true } = Astro.props; typespec.io - - + +
diff --git a/website/src/middleware.ts b/website/src/middleware.ts new file mode 100644 index 00000000000..b20ef79fe3f --- /dev/null +++ b/website/src/middleware.ts @@ -0,0 +1,21 @@ +import { defineMiddleware } from "astro:middleware"; + +export const onRequest = defineMiddleware(async (context, next) => { + const response = await next(); + const contentType = response.headers.get("content-type"); + + // Set charset=utf-8 for all HTML responses + if (contentType?.includes("text/html")) { + response.headers.set("content-type", "text/html; charset=utf-8"); + } + + // Disable directory indexing for the configuration page + if ( + context.url.pathname === "/docs/handbook/configuration/configuration/" || + context.url.pathname === "/docs/handbook/configuration/configuration" + ) { + response.headers.set("X-Robots-Tag", "noindex"); + } + + return response; +}); From 88fdab9136269f774921851c40ba7a4325262bcd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 02:39:37 +0000 Subject: [PATCH 2/7] refactor: simplify pathname check in middleware Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/7dfd7541-76dc-4f3c-9397-bdedcfaca7a0 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- website/src/middleware.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/website/src/middleware.ts b/website/src/middleware.ts index b20ef79fe3f..4748975cb5a 100644 --- a/website/src/middleware.ts +++ b/website/src/middleware.ts @@ -10,10 +10,8 @@ export const onRequest = defineMiddleware(async (context, next) => { } // Disable directory indexing for the configuration page - if ( - context.url.pathname === "/docs/handbook/configuration/configuration/" || - context.url.pathname === "/docs/handbook/configuration/configuration" - ) { + const normalizedPath = context.url.pathname.replace(/\/$/, ""); + if (normalizedPath === "/docs/handbook/configuration/configuration") { response.headers.set("X-Robots-Tag", "noindex"); } From 6abeaf058ff96540242a63bf81b1db5762c320e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:46:55 +0000 Subject: [PATCH 3/7] feat(website): add charset=utf-8 to content-type for all text-based responses Extends the Astro middleware to set charset=utf-8 on all text-based content types (text/*, XML, JSON, JavaScript, shell scripts), not just text/html. Binary types like video/mp4 are correctly excluded. Fixes missing charset on /img/favicon.svg, /install.sh, /sitemap-index.xml, and /playground. Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/51f75269-4872-4fa8-9506-d7177bd4b470 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- website/src/middleware.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/website/src/middleware.ts b/website/src/middleware.ts index 4748975cb5a..d2b491b3572 100644 --- a/website/src/middleware.ts +++ b/website/src/middleware.ts @@ -1,12 +1,26 @@ import { defineMiddleware } from "astro:middleware"; +/** + * Returns true when the MIME type is text-based and should carry charset=utf-8. + * Covers text/* (html, plain, css, …), XML flavours (image/svg+xml, + * application/xml, …), JSON, and JavaScript / shell-script types. + */ +function isTextBasedContentType(contentType: string): boolean { + if (contentType.startsWith("text/")) return true; + if (contentType.includes("/xml") || contentType.includes("+xml")) return true; + if (contentType.includes("/json") || contentType.includes("+json")) return true; + if (contentType.includes("/javascript")) return true; + if (contentType.includes("/x-sh")) return true; + return false; +} + export const onRequest = defineMiddleware(async (context, next) => { const response = await next(); const contentType = response.headers.get("content-type"); - // Set charset=utf-8 for all HTML responses - if (contentType?.includes("text/html")) { - response.headers.set("content-type", "text/html; charset=utf-8"); + // Add charset=utf-8 for text-based content types that don't already specify one + if (contentType && !contentType.includes("charset") && isTextBasedContentType(contentType)) { + response.headers.set("content-type", `${contentType}; charset=utf-8`); } // Disable directory indexing for the configuration page From e8a771734c0b12cfd30184d517f905faad6aaba1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 20:50:35 +0000 Subject: [PATCH 4/7] refactor: compute 1ds-init.js integrity hash dynamically at build time Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/fe861958-6bef-43f5-b3e4-00d92faf7338 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- website/src/layouts/base-layout.astro | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/website/src/layouts/base-layout.astro b/website/src/layouts/base-layout.astro index 8aef70ced5d..4719c13c1a4 100644 --- a/website/src/layouts/base-layout.astro +++ b/website/src/layouts/base-layout.astro @@ -3,12 +3,20 @@ import "@site/src/css/custom.css"; import Header from "@site/src/components/header/header.astro"; import Footer from "@site/src/components/footer/footer.astro"; import { baseUrl } from "@typespec/astro-utils/utils/base-url"; +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; export interface Props { /** Whether to render the footer @default true */ footer?: boolean; } const { footer = true } = Astro.props; + +// Compute subresource integrity hash for 1ds-init.js at build time +const initJsPath = resolve(process.cwd(), "public/1ds-init.js"); +const initJsContent = readFileSync(initJsPath); +const initJsIntegrity = `sha384-${createHash("sha384").update(initJsContent).digest("base64")}`; --- @@ -22,10 +30,8 @@ const { footer = true } = Astro.props; src="https://consentdeliveryfd.azurefd.net/mscc/lib/v2/wcp-consent.js" crossorigin="anonymous" is:inline> - +
From 5a11b8bebd050bcb885faf449db3bd9f146d69df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 01:31:10 +0000 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20trim=20to=20script=20integrity?= =?UTF-8?q?=20fix=20only=20=E2=80=94=20remove=20middleware=20and=20robots?= =?UTF-8?q?=20noindex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/9f4023b2-5b8a-41d3-90d3-edea359b6b4c Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- website/astro.config.mjs | 7 +++- .../handbook/configuration/configuration.mdx | 5 --- website/src/middleware.ts | 33 ------------------- 3 files changed, 6 insertions(+), 39 deletions(-) delete mode 100644 website/src/middleware.ts diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 84390e5b504..6a6111ea411 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -6,6 +6,7 @@ import { processSidebar } from "@typespec/astro-utils/sidebar"; import astroExpressiveCode from "astro-expressive-code"; import rehypeAstroRelativeMarkdownLinks from "astro-rehype-relative-markdown-links"; import { defineConfig } from "astro/config"; +import { createHash } from "node:crypto"; import { readdirSync, readFileSync } from "node:fs"; import { resolve } from "pathe"; import rehypeMermaid from "rehype-mermaid"; @@ -43,6 +44,10 @@ const latestReleaseNote = getLatestReleaseNoteSlug(); const base = process.env.TYPESPEC_WEBSITE_BASE_PATH ?? "/"; +// Compute subresource integrity hash for 1ds-init.js at build time +const initJsContent = readFileSync(resolve(import.meta.dirname, "public/1ds-init.js")); +const initJsIntegrity = `sha384-${createHash("sha384").update(initJsContent).digest("base64")}`; + // https://astro.build/config export default defineConfig({ base, @@ -107,7 +112,7 @@ export default defineConfig({ type: "module", async: true, src: "1ds-init.js", - integrity: "sha384-idYZGvEvTNaaqBGjIeT/diHjEM13+k+Utpb4k+TjTW/ui8yYALU1FXvgk+lG/EZG", + integrity: initJsIntegrity, }, }, ], diff --git a/website/src/content/docs/docs/handbook/configuration/configuration.mdx b/website/src/content/docs/docs/handbook/configuration/configuration.mdx index 68a491bc92d..2c53ec7ec14 100644 --- a/website/src/content/docs/docs/handbook/configuration/configuration.mdx +++ b/website/src/content/docs/docs/handbook/configuration/configuration.mdx @@ -1,10 +1,5 @@ --- title: Configuration -head: - - tag: meta - attrs: - name: robots - content: noindex --- import { FileTree } from "@astrojs/starlight/components"; diff --git a/website/src/middleware.ts b/website/src/middleware.ts deleted file mode 100644 index d2b491b3572..00000000000 --- a/website/src/middleware.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { defineMiddleware } from "astro:middleware"; - -/** - * Returns true when the MIME type is text-based and should carry charset=utf-8. - * Covers text/* (html, plain, css, …), XML flavours (image/svg+xml, - * application/xml, …), JSON, and JavaScript / shell-script types. - */ -function isTextBasedContentType(contentType: string): boolean { - if (contentType.startsWith("text/")) return true; - if (contentType.includes("/xml") || contentType.includes("+xml")) return true; - if (contentType.includes("/json") || contentType.includes("+json")) return true; - if (contentType.includes("/javascript")) return true; - if (contentType.includes("/x-sh")) return true; - return false; -} - -export const onRequest = defineMiddleware(async (context, next) => { - const response = await next(); - const contentType = response.headers.get("content-type"); - - // Add charset=utf-8 for text-based content types that don't already specify one - if (contentType && !contentType.includes("charset") && isTextBasedContentType(contentType)) { - response.headers.set("content-type", `${contentType}; charset=utf-8`); - } - - // Disable directory indexing for the configuration page - const normalizedPath = context.url.pathname.replace(/\/$/, ""); - if (normalizedPath === "/docs/handbook/configuration/configuration") { - response.headers.set("X-Robots-Tag", "noindex"); - } - - return response; -}); From 7acd6ebe8691f515ad3527214ee38a9ba4ad83e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 02:26:37 +0000 Subject: [PATCH 6/7] refactor: factor out SRI hash computation into shared helper Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/38238d3a-1975-4b8b-bc41-17c6aab6fac9 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- website/astro.config.mjs | 6 ++---- website/src/layouts/base-layout.astro | 9 ++------- website/src/utils/sri-hash.ts | 13 +++++++++++++ 3 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 website/src/utils/sri-hash.ts diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 6a6111ea411..324902b35e3 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -6,12 +6,12 @@ import { processSidebar } from "@typespec/astro-utils/sidebar"; import astroExpressiveCode from "astro-expressive-code"; import rehypeAstroRelativeMarkdownLinks from "astro-rehype-relative-markdown-links"; import { defineConfig } from "astro/config"; -import { createHash } from "node:crypto"; import { readdirSync, readFileSync } from "node:fs"; import { resolve } from "pathe"; import rehypeMermaid from "rehype-mermaid"; import remarkHeadingID from "remark-heading-id"; import current from "./src/content/current-sidebar"; +import { computeSriHash } from "./src/utils/sri-hash"; /** Scan the release-notes directory and return the slug of the latest release note. */ function getLatestReleaseNoteSlug() { @@ -44,9 +44,7 @@ const latestReleaseNote = getLatestReleaseNoteSlug(); const base = process.env.TYPESPEC_WEBSITE_BASE_PATH ?? "/"; -// Compute subresource integrity hash for 1ds-init.js at build time -const initJsContent = readFileSync(resolve(import.meta.dirname, "public/1ds-init.js")); -const initJsIntegrity = `sha384-${createHash("sha384").update(initJsContent).digest("base64")}`; +const initJsIntegrity = computeSriHash("1ds-init.js"); // https://astro.build/config export default defineConfig({ diff --git a/website/src/layouts/base-layout.astro b/website/src/layouts/base-layout.astro index 4719c13c1a4..4d22634c126 100644 --- a/website/src/layouts/base-layout.astro +++ b/website/src/layouts/base-layout.astro @@ -3,9 +3,7 @@ import "@site/src/css/custom.css"; import Header from "@site/src/components/header/header.astro"; import Footer from "@site/src/components/footer/footer.astro"; import { baseUrl } from "@typespec/astro-utils/utils/base-url"; -import { createHash } from "node:crypto"; -import { readFileSync } from "node:fs"; -import { resolve } from "node:path"; +import { computeSriHash } from "@site/src/utils/sri-hash"; export interface Props { /** Whether to render the footer @default true */ @@ -13,10 +11,7 @@ export interface Props { } const { footer = true } = Astro.props; -// Compute subresource integrity hash for 1ds-init.js at build time -const initJsPath = resolve(process.cwd(), "public/1ds-init.js"); -const initJsContent = readFileSync(initJsPath); -const initJsIntegrity = `sha384-${createHash("sha384").update(initJsContent).digest("base64")}`; +const initJsIntegrity = computeSriHash("1ds-init.js"); --- diff --git a/website/src/utils/sri-hash.ts b/website/src/utils/sri-hash.ts new file mode 100644 index 00000000000..6ca1ee79c6f --- /dev/null +++ b/website/src/utils/sri-hash.ts @@ -0,0 +1,13 @@ +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +/** + * Compute a SHA-384 subresource integrity (SRI) hash for the given file, + * relative to the website `public/` directory. + */ +export function computeSriHash(publicRelativePath: string): string { + const absPath = resolve(process.cwd(), "public", publicRelativePath); + const content = readFileSync(absPath); + return `sha384-${createHash("sha384").update(content).digest("base64")}`; +} From f8075231a06a628322b649df970889bc0d71c223 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 02:41:21 +0000 Subject: [PATCH 7/7] fix: use process.cwd() in SRI helper for reliable path resolution during pre-render Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/38238d3a-1975-4b8b-bc41-17c6aab6fac9 Co-authored-by: markcowl <1054056+markcowl@users.noreply.github.com> --- website/src/utils/sri-hash.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/src/utils/sri-hash.ts b/website/src/utils/sri-hash.ts index 6ca1ee79c6f..002686ab3d6 100644 --- a/website/src/utils/sri-hash.ts +++ b/website/src/utils/sri-hash.ts @@ -5,6 +5,9 @@ import { resolve } from "node:path"; /** * Compute a SHA-384 subresource integrity (SRI) hash for the given file, * relative to the website `public/` directory. + * + * Uses `process.cwd()` because Astro always runs from the website root, + * and `import.meta.dirname` is unreliable after bundling during pre-render. */ export function computeSriHash(publicRelativePath: string): string { const absPath = resolve(process.cwd(), "public", publicRelativePath);