From 55a85e14add05ffe4909b5a83045e4b0b01db41a Mon Sep 17 00:00:00 2001 From: jessiemongeon1 Date: Mon, 4 May 2026 12:06:29 -0500 Subject: [PATCH 1/9] more fixes for afdocs --- book/sidebar.yml | 45 +++++------- reference/index-syntax.md | 2 +- reference/sidebar.yml | 10 ++- site/serve-with-rewrites.js | 137 +++++++++++++++++++++++++++++++++++ site/src/plugins/llms-txt.ts | 58 ++++++++++++--- 5 files changed, 212 insertions(+), 40 deletions(-) create mode 100644 site/serve-with-rewrites.js diff --git a/book/sidebar.yml b/book/sidebar.yml index 0c39edf3..02ee1a11 100644 --- a/book/sidebar.yml +++ b/book/sidebar.yml @@ -180,32 +180,20 @@ bookSidebar: id: programmability/display - label: Events id: programmability/events - # - label: Balance & Coin - # id: programmability/balance-and-coin - # type: doc + - label: Balance & Coin + id: programmability/balance-and-coin - label: 'Pattern: Hot Potato' id: programmability/hot-potato-pattern - # - label: '8.18 Pattern: Request' - # id: programmability/request-pattern - # type: doc - # - label: '8.19 Pattern: Object Capability' - # id: programmability/object-capability-pattern - # type: doc - # - label: '8.20 Package Upgrades' - # id: programmability/package-upgrades - # type: doc - # - label: '8.21 Transaction Blocks' - # id: programmability/transaction-block - # type: doc - # - label: '8.22 Authorization Patterns' - # id: programmability/authorization-patterns - # type: doc - # - label: '8.23 Cryptography and Hashing' - # id: programmability/cryptography-and-hashing - # type: doc - # - label: '8.24 Randomness' - # id: programmability/cryptography-and-hashing - # type: doc + - label: 'Pattern: Object Capability' + id: programmability/object-capability + - label: Authorization Patterns + id: programmability/authorization-patterns + - label: Cryptography and Hashing + id: programmability/cryptography-and-hashing + - label: Randomness + id: programmability/randomness + - label: Fast Path + id: programmability/fast-path - label: BCS id: programmability/bcs - type: category @@ -249,6 +237,9 @@ bookSidebar: - type: category label: Guides enumerate: false + link: + id: guides/index + type: doc items: - label: 2024 Migration Guide id: guides/2024-migration-guide @@ -260,10 +251,8 @@ bookSidebar: id: guides/better-error-handling - label: Code Quality Checklist id: guides/code-quality-checklist - # - label: Open-sourcing Libraries - # id: guides/open-sourcing-libraries - # - label: Creating an NFT Collection - # id: guides/creating-an-nft-collection + - label: Open-sourcing Libraries + id: guides/open-sourcing-libraries - type: category label: Appendix enumerate: false diff --git a/reference/index-syntax.md b/reference/index-syntax.md index f3cec172..df7826ca 100644 --- a/reference/index-syntax.md +++ b/reference/index-syntax.md @@ -101,7 +101,7 @@ fun borrow_first(input: &Vs): &u64 { &input.vs[0].v[0] // translates to `vector::borrow(&vector::borrow(&input.vs, 0).v, 0)` } -```` +``` ### Index Functions Take Flexible Arguments diff --git a/reference/sidebar.yml b/reference/sidebar.yml index 831abc25..9a6792c8 100644 --- a/reference/sidebar.yml +++ b/reference/sidebar.yml @@ -75,8 +75,14 @@ referenceSidebar: id: constants - label: 11. Generics id: generics - - label: 12. Abilities - id: abilities + - type: category + label: 12. Abilities + link: + id: abilities + type: doc + items: + - label: 12.1 Sui Object + id: abilities/object - label: 13. Uses and Aliases id: uses - type: category diff --git a/site/serve-with-rewrites.js b/site/serve-with-rewrites.js new file mode 100644 index 00000000..66eec2d8 --- /dev/null +++ b/site/serve-with-rewrites.js @@ -0,0 +1,137 @@ +/* +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +*/ + +/** + * Local server that serves the Docusaurus build with proper headers + * for markdown files, llms.txt, and content negotiation. + * + * Usage: node serve-with-rewrites.js [port] + */ + +const http = require('http'); +const fs = require('fs'); +const path = require('path'); +const url = require('url'); + +const PORT = process.argv[2] || 3001; +const BUILD_DIR = path.join(__dirname, 'build'); + +const MIME_TYPES = { + '.html': 'text/html; charset=utf-8', + '.js': 'text/javascript', + '.css': 'text/css', + '.json': 'application/json', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.gif': 'image/gif', + '.svg': 'image/svg+xml', + '.ico': 'image/x-icon', + '.md': 'text/markdown; charset=utf-8', + '.txt': 'text/plain; charset=utf-8', + '.ebnf': 'text/plain; charset=utf-8', + '.woff': 'font/woff', + '.woff2': 'font/woff2', + '.pdf': 'application/pdf', +}; + +function getContentType(filePath) { + const ext = path.extname(filePath).toLowerCase(); + return MIME_TYPES[ext] || 'application/octet-stream'; +} + +function getCacheControl(filePath) { + const ext = path.extname(filePath).toLowerCase(); + if (ext === '.html' || ext === '.txt' || ext === '.md' || ext === '.ebnf') { + return 'public, max-age=0, must-revalidate'; + } + return 'public, max-age=3600'; +} + +/** + * Checks whether the request Accept header includes text/markdown. + */ +function acceptsMarkdown(req) { + const accept = req.headers['accept'] || ''; + return accept.includes('text/markdown'); +} + +/** + * Tries to resolve a markdown file for the given pathname. + * Maps e.g. "/" -> "index.md", "/foreword" -> "foreword.md", + * "/move-basics/module" -> "move-basics/module.md". + */ +function resolveMarkdownFile(pathname) { + const clean = pathname.replace(/\/+$/, '') || '/'; + + if (clean === '/') { + const candidate = path.join(BUILD_DIR, 'index.md'); + if (fs.existsSync(candidate)) return candidate; + return null; + } + + // Try .md first, then /index.md + const asMd = path.join(BUILD_DIR, clean + '.md'); + if (fs.existsSync(asMd)) return asMd; + + const asIndex = path.join(BUILD_DIR, clean, 'index.md'); + if (fs.existsSync(asIndex)) return asIndex; + + return null; +} + +const server = http.createServer((req, res) => { + const parsedUrl = url.parse(req.url); + let pathname = parsedUrl.pathname; + + // Content negotiation: serve markdown when Accept: text/markdown + if (acceptsMarkdown(req)) { + const mdFile = resolveMarkdownFile(pathname); + if (mdFile) { + const content = fs.readFileSync(mdFile); + res.writeHead(200, { + 'Content-Type': 'text/markdown; charset=utf-8', + 'Content-Disposition': 'inline', + 'Cache-Control': 'public, max-age=0, must-revalidate', + }); + res.end(content); + return; + } + } + + // Resolve file path + let filePath = path.join(BUILD_DIR, pathname); + + if (!fs.existsSync(filePath)) { + // Try index.html for directory-style routes + const indexPath = path.join(BUILD_DIR, pathname, 'index.html'); + if (fs.existsSync(indexPath)) { + filePath = indexPath; + } else { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end('404 Not Found'); + return; + } + } else if (fs.statSync(filePath).isDirectory()) { + filePath = path.join(filePath, 'index.html'); + } + + if (!fs.existsSync(filePath)) { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end('404 Not Found'); + return; + } + + const content = fs.readFileSync(filePath); + res.writeHead(200, { + 'Content-Type': getContentType(filePath), + 'Content-Disposition': 'inline', + 'Cache-Control': getCacheControl(filePath), + }); + res.end(content); +}); + +server.listen(PORT, () => { + console.log(`Serving build at http://localhost:${PORT}/`); +}); diff --git a/site/src/plugins/llms-txt.ts b/site/src/plugins/llms-txt.ts index e69da36a..4a097c9c 100644 --- a/site/src/plugins/llms-txt.ts +++ b/site/src/plugins/llms-txt.ts @@ -96,8 +96,13 @@ function extractAnchor(fileContent: string, anchor: string): string | null { return fileContent.slice(contentStart, endIdx).trimEnd(); } +function stripHtmlComments(content: string): string { + return content.replace(//g, '').replace(/\n{3,}/g, '\n\n'); +} + function resolveCodeIncludes(content: string): string { - return content.replace( + const stripped = stripHtmlComments(content); + return stripped.replace( /^(```\w*)\s+(?:title="[^"]*"\s+)?file=(\S+?)(?:\s+anchor=(\S+))?\s*\n[\s\S]*?^```/gm, (match, fence, filePath, anchor) => { const absPath = path.join(ROOT, filePath); @@ -190,17 +195,10 @@ function generateFiles(outDir: string) { // llms.txt const llmsTxt = `# The Move Book -> A comprehensive guide to the Move programming language on Sui. +> A comprehensive guide to the Move programming language on Sui. Additional resources: [EBNF grammar](${siteUrl}/move.ebnf), [Move semantics](${siteUrl}/move-semantics.md), [full book for large-context models](${siteUrl}/llms-full.txt). The Move Book covers the Move language fundamentals, Sui object model, advanced programmability patterns, and testing. The Move Reference provides formal language specification. -## Move Language - -- [Move language syntax (EBNF grammar)](${siteUrl}/move.ebnf) -- [Move semantics](${siteUrl}/move-semantics.md) -- [Best practices](${siteUrl}/guides/code-quality-checklist.md) -- [Full book content for large-context models](${siteUrl}/llms-full.txt) - ## Book ${bookDocs.map(buildIndexLine).join('\n')} @@ -209,11 +207,20 @@ ${bookDocs.map(buildIndexLine).join('\n')} ${refDocs.map(buildIndexLine).join('\n')} +## Site + +- [Search](${siteUrl}/search.md) + `; fs.writeFileSync(path.join(outDir, 'llms.txt'), llmsTxt); console.log('[llms-txt] Built llms.txt'); + // Generate search.md for the custom search page + const searchMd = llmsTxtDirective + '# Search\n\nSearch across all Move Book and Move Reference documentation.\n'; + fs.writeFileSync(path.join(outDir, 'search.md'), searchMd); + console.log('[llms-txt] Generated search.md'); + // llms-full.txt const fullTxt = [ '# The Move Book\n', @@ -274,6 +281,39 @@ export default function pluginLlmsTxt(): Plugin { }, }); + // Content negotiation: serve markdown when Accept: text/markdown + middlewares.unshift({ + name: 'content-negotiation', + middleware: (req: any, res: any, next: any) => { + const accept = req.headers['accept'] || ''; + if (!accept.includes('text/markdown')) return next(); + + const url = (req.url?.split('?')[0] || '').replace(/\/+$/, '') || '/'; + let mdPath: string | null = null; + + if (url === '/') { + const candidate = path.join(staticDir, 'index.md'); + if (fs.existsSync(candidate)) mdPath = candidate; + } else { + const asMd = path.join(staticDir, url + '.md'); + if (fs.existsSync(asMd)) mdPath = asMd; + if (!mdPath) { + const asIndex = path.join(staticDir, url, 'index.md'); + if (fs.existsSync(asIndex)) mdPath = asIndex; + } + } + + if (mdPath) { + res.setHeader('Content-Type', 'text/markdown; charset=utf-8'); + res.setHeader('Content-Disposition', 'inline'); + res.setHeader('Cache-Control', 'public, max-age=0, must-revalidate'); + res.end(fs.readFileSync(mdPath, 'utf-8')); + return; + } + next(); + }, + }); + // Return 404 for non-existent pages (prevent SPA fallback) middlewares.push({ name: 'proper-404', From 89c33f71281ca854eac75f93889d0d1851353033 Mon Sep 17 00:00:00 2001 From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> Date: Mon, 4 May 2026 12:11:54 -0500 Subject: [PATCH 2/9] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- site/serve-with-rewrites.js | 48 ++++++++++++++++++++++++++++-------- site/src/plugins/llms-txt.ts | 9 ++++++- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/site/serve-with-rewrites.js b/site/serve-with-rewrites.js index 66eec2d8..db06e1f8 100644 --- a/site/serve-with-rewrites.js +++ b/site/serve-with-rewrites.js @@ -17,6 +17,8 @@ const url = require('url'); const PORT = process.argv[2] || 3001; const BUILD_DIR = path.join(__dirname, 'build'); +const BUILD_ROOT = path.resolve(BUILD_DIR); +const BUILD_ROOT_WITH_SEP = BUILD_ROOT.endsWith(path.sep) ? BUILD_ROOT : BUILD_ROOT + path.sep; const MIME_TYPES = { '.html': 'text/html; charset=utf-8', @@ -57,6 +59,21 @@ function acceptsMarkdown(req) { return accept.includes('text/markdown'); } +function resolveUnderBuildDir(relativePath) { + let decodedPath; + try { + decodedPath = decodeURIComponent(relativePath || '/'); + } catch (e) { + return null; + } + + const resolved = path.resolve(BUILD_ROOT, '.' + decodedPath); + if (resolved !== BUILD_ROOT && !resolved.startsWith(BUILD_ROOT_WITH_SEP)) { + return null; + } + return resolved; +} + /** * Tries to resolve a markdown file for the given pathname. * Maps e.g. "/" -> "index.md", "/foreword" -> "foreword.md", @@ -66,17 +83,17 @@ function resolveMarkdownFile(pathname) { const clean = pathname.replace(/\/+$/, '') || '/'; if (clean === '/') { - const candidate = path.join(BUILD_DIR, 'index.md'); - if (fs.existsSync(candidate)) return candidate; + const candidate = resolveUnderBuildDir('/index.md'); + if (candidate && fs.existsSync(candidate)) return candidate; return null; } // Try .md first, then /index.md - const asMd = path.join(BUILD_DIR, clean + '.md'); - if (fs.existsSync(asMd)) return asMd; + const asMd = resolveUnderBuildDir(clean + '.md'); + if (asMd && fs.existsSync(asMd)) return asMd; - const asIndex = path.join(BUILD_DIR, clean, 'index.md'); - if (fs.existsSync(asIndex)) return asIndex; + const asIndex = resolveUnderBuildDir(path.join(clean, 'index.md')); + if (asIndex && fs.existsSync(asIndex)) return asIndex; return null; } @@ -101,12 +118,17 @@ const server = http.createServer((req, res) => { } // Resolve file path - let filePath = path.join(BUILD_DIR, pathname); + let filePath = resolveUnderBuildDir(pathname); + if (!filePath) { + res.writeHead(403, { 'Content-Type': 'text/plain' }); + res.end('403 Forbidden'); + return; + } if (!fs.existsSync(filePath)) { // Try index.html for directory-style routes - const indexPath = path.join(BUILD_DIR, pathname, 'index.html'); - if (fs.existsSync(indexPath)) { + const indexPath = resolveUnderBuildDir(path.join(pathname, 'index.html')); + if (indexPath && fs.existsSync(indexPath)) { filePath = indexPath; } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); @@ -114,7 +136,13 @@ const server = http.createServer((req, res) => { return; } } else if (fs.statSync(filePath).isDirectory()) { - filePath = path.join(filePath, 'index.html'); + const directoryIndexPath = resolveUnderBuildDir(path.join(pathname, 'index.html')); + if (!directoryIndexPath) { + res.writeHead(403, { 'Content-Type': 'text/plain' }); + res.end('403 Forbidden'); + return; + } + filePath = directoryIndexPath; } if (!fs.existsSync(filePath)) { diff --git a/site/src/plugins/llms-txt.ts b/site/src/plugins/llms-txt.ts index 4a097c9c..c75f413a 100644 --- a/site/src/plugins/llms-txt.ts +++ b/site/src/plugins/llms-txt.ts @@ -97,7 +97,14 @@ function extractAnchor(fileContent: string, anchor: string): string | null { } function stripHtmlComments(content: string): string { - return content.replace(//g, '').replace(/\n{3,}/g, '\n\n'); + let current = content; + let previous: string; + do { + previous = current; + current = current.replace(//g, ''); + } while (current !== previous); + + return current.replace(/\n{3,}/g, '\n\n'); } function resolveCodeIncludes(content: string): string { From 0ff53dd1bf4d03e6b35849be9d7d350889b131f5 Mon Sep 17 00:00:00 2001 From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> Date: Mon, 4 May 2026 12:15:44 -0500 Subject: [PATCH 3/9] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- site/serve-with-rewrites.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/site/serve-with-rewrites.js b/site/serve-with-rewrites.js index db06e1f8..2ea762b5 100644 --- a/site/serve-with-rewrites.js +++ b/site/serve-with-rewrites.js @@ -19,6 +19,10 @@ const PORT = process.argv[2] || 3001; const BUILD_DIR = path.join(__dirname, 'build'); const BUILD_ROOT = path.resolve(BUILD_DIR); const BUILD_ROOT_WITH_SEP = BUILD_ROOT.endsWith(path.sep) ? BUILD_ROOT : BUILD_ROOT + path.sep; +const CANONICAL_BUILD_ROOT = fs.realpathSync(BUILD_ROOT); +const CANONICAL_BUILD_ROOT_WITH_SEP = CANONICAL_BUILD_ROOT.endsWith(path.sep) + ? CANONICAL_BUILD_ROOT + : CANONICAL_BUILD_ROOT + path.sep; const MIME_TYPES = { '.html': 'text/html; charset=utf-8', @@ -67,11 +71,27 @@ function resolveUnderBuildDir(relativePath) { return null; } - const resolved = path.resolve(BUILD_ROOT, '.' + decodedPath); + const normalizedPath = decodedPath.replace(/\\/g, '/'); + const resolved = path.resolve(BUILD_ROOT, '.' + normalizedPath); if (resolved !== BUILD_ROOT && !resolved.startsWith(BUILD_ROOT_WITH_SEP)) { return null; } - return resolved; + + let canonicalResolved; + try { + canonicalResolved = fs.realpathSync(resolved); + } catch (e) { + return null; + } + + if ( + canonicalResolved !== CANONICAL_BUILD_ROOT && + !canonicalResolved.startsWith(CANONICAL_BUILD_ROOT_WITH_SEP) + ) { + return null; + } + + return canonicalResolved; } /** From b8ce9ebdc4dc1fa06c92f4d0aa44b3573ec63696 Mon Sep 17 00:00:00 2001 From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> Date: Mon, 4 May 2026 12:15:52 -0500 Subject: [PATCH 4/9] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- site/serve-with-rewrites.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/site/serve-with-rewrites.js b/site/serve-with-rewrites.js index 2ea762b5..1a995e0d 100644 --- a/site/serve-with-rewrites.js +++ b/site/serve-with-rewrites.js @@ -63,6 +63,17 @@ function acceptsMarkdown(req) { return accept.includes('text/markdown'); } +function isCanonicalPathUnderBuildRoot(targetPath) { + try { + const realTarget = fs.realpathSync.native ? fs.realpathSync.native(targetPath) : fs.realpathSync(targetPath); + const realBuildRoot = fs.realpathSync.native ? fs.realpathSync.native(BUILD_ROOT) : fs.realpathSync(BUILD_ROOT); + const realBuildRootWithSep = realBuildRoot.endsWith(path.sep) ? realBuildRoot : realBuildRoot + path.sep; + return realTarget === realBuildRoot || realTarget.startsWith(realBuildRootWithSep); + } catch (e) { + return false; + } +} + function resolveUnderBuildDir(relativePath) { let decodedPath; try { @@ -171,6 +182,12 @@ const server = http.createServer((req, res) => { return; } + if (!isCanonicalPathUnderBuildRoot(filePath)) { + res.writeHead(403, { 'Content-Type': 'text/plain' }); + res.end('403 Forbidden'); + return; + } + const content = fs.readFileSync(filePath); res.writeHead(200, { 'Content-Type': getContentType(filePath), From 39c915ba653d11c603b69724abf8028a37efeaad Mon Sep 17 00:00:00 2001 From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> Date: Mon, 4 May 2026 12:16:52 -0500 Subject: [PATCH 5/9] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- site/serve-with-rewrites.js | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/site/serve-with-rewrites.js b/site/serve-with-rewrites.js index 1a995e0d..c6d36108 100644 --- a/site/serve-with-rewrites.js +++ b/site/serve-with-rewrites.js @@ -166,14 +166,33 @@ const server = http.createServer((req, res) => { res.end('404 Not Found'); return; } - } else if (fs.statSync(filePath).isDirectory()) { - const directoryIndexPath = resolveUnderBuildDir(path.join(pathname, 'index.html')); - if (!directoryIndexPath) { + } else { + let canonicalFilePath; + try { + canonicalFilePath = fs.realpathSync(filePath); + } catch (e) { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end('404 Not Found'); + return; + } + + if (canonicalFilePath !== BUILD_ROOT && !canonicalFilePath.startsWith(BUILD_ROOT_WITH_SEP)) { res.writeHead(403, { 'Content-Type': 'text/plain' }); res.end('403 Forbidden'); return; } - filePath = directoryIndexPath; + + filePath = canonicalFilePath; + + if (fs.statSync(filePath).isDirectory()) { + const directoryIndexPath = resolveUnderBuildDir(path.join(pathname, 'index.html')); + if (!directoryIndexPath) { + res.writeHead(403, { 'Content-Type': 'text/plain' }); + res.end('403 Forbidden'); + return; + } + filePath = directoryIndexPath; + } } if (!fs.existsSync(filePath)) { From 1b5592cfe77513f6b8b46b045255bb1f75c42d5a Mon Sep 17 00:00:00 2001 From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> Date: Mon, 4 May 2026 12:17:48 -0500 Subject: [PATCH 6/9] Potential fix for pull request finding 'CodeQL / Uncontrolled data used in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- site/serve-with-rewrites.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/site/serve-with-rewrites.js b/site/serve-with-rewrites.js index c6d36108..34a61c3e 100644 --- a/site/serve-with-rewrites.js +++ b/site/serve-with-rewrites.js @@ -74,6 +74,19 @@ function isCanonicalPathUnderBuildRoot(targetPath) { } } +function isSafeRequestPathname(pathname) { + if (typeof pathname !== 'string' || pathname.length === 0) return false; + if (!pathname.startsWith('/')) return false; + if (pathname.includes('\0') || pathname.includes('\\')) return false; + + const segments = pathname.split('/'); + for (const segment of segments) { + if (segment === '.' || segment === '..') return false; + } + + return true; +} + function resolveUnderBuildDir(relativePath) { let decodedPath; try { @@ -133,6 +146,12 @@ const server = http.createServer((req, res) => { const parsedUrl = url.parse(req.url); let pathname = parsedUrl.pathname; + if (!isSafeRequestPathname(pathname)) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end('400 Bad Request'); + return; + } + // Content negotiation: serve markdown when Accept: text/markdown if (acceptsMarkdown(req)) { const mdFile = resolveMarkdownFile(pathname); From c6f060472068758bdc1c661a3da36e8d05e88a3e Mon Sep 17 00:00:00 2001 From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> Date: Mon, 4 May 2026 12:22:58 -0500 Subject: [PATCH 7/9] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- site/serve-with-rewrites.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/site/serve-with-rewrites.js b/site/serve-with-rewrites.js index 34a61c3e..e800d2d9 100644 --- a/site/serve-with-rewrites.js +++ b/site/serve-with-rewrites.js @@ -126,6 +126,17 @@ function resolveUnderBuildDir(relativePath) { function resolveMarkdownFile(pathname) { const clean = pathname.replace(/\/+$/, '') || '/'; + // Explicit allowlist validation to prevent path traversal and make + // user-controlled path constraints obvious to static analysis. + if ( + !clean.startsWith('/') || + clean.includes('\0') || + /(^|\/)\.\.(\/|$)/.test(clean) || + !/^\/[A-Za-z0-9._/-]*$/.test(clean) + ) { + return null; + } + if (clean === '/') { const candidate = resolveUnderBuildDir('/index.md'); if (candidate && fs.existsSync(candidate)) return candidate; @@ -195,7 +206,10 @@ const server = http.createServer((req, res) => { return; } - if (canonicalFilePath !== BUILD_ROOT && !canonicalFilePath.startsWith(BUILD_ROOT_WITH_SEP)) { + if ( + canonicalFilePath !== CANONICAL_BUILD_ROOT && + !canonicalFilePath.startsWith(CANONICAL_BUILD_ROOT_WITH_SEP) + ) { res.writeHead(403, { 'Content-Type': 'text/plain' }); res.end('403 Forbidden'); return; From 2dcaad6c95440f11b9bf07ebffd241db2f502491 Mon Sep 17 00:00:00 2001 From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> Date: Tue, 5 May 2026 10:08:12 -0500 Subject: [PATCH 8/9] Update sidebar.yml --- book/sidebar.yml | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/book/sidebar.yml b/book/sidebar.yml index 02ee1a11..0c39edf3 100644 --- a/book/sidebar.yml +++ b/book/sidebar.yml @@ -180,20 +180,32 @@ bookSidebar: id: programmability/display - label: Events id: programmability/events - - label: Balance & Coin - id: programmability/balance-and-coin + # - label: Balance & Coin + # id: programmability/balance-and-coin + # type: doc - label: 'Pattern: Hot Potato' id: programmability/hot-potato-pattern - - label: 'Pattern: Object Capability' - id: programmability/object-capability - - label: Authorization Patterns - id: programmability/authorization-patterns - - label: Cryptography and Hashing - id: programmability/cryptography-and-hashing - - label: Randomness - id: programmability/randomness - - label: Fast Path - id: programmability/fast-path + # - label: '8.18 Pattern: Request' + # id: programmability/request-pattern + # type: doc + # - label: '8.19 Pattern: Object Capability' + # id: programmability/object-capability-pattern + # type: doc + # - label: '8.20 Package Upgrades' + # id: programmability/package-upgrades + # type: doc + # - label: '8.21 Transaction Blocks' + # id: programmability/transaction-block + # type: doc + # - label: '8.22 Authorization Patterns' + # id: programmability/authorization-patterns + # type: doc + # - label: '8.23 Cryptography and Hashing' + # id: programmability/cryptography-and-hashing + # type: doc + # - label: '8.24 Randomness' + # id: programmability/cryptography-and-hashing + # type: doc - label: BCS id: programmability/bcs - type: category @@ -237,9 +249,6 @@ bookSidebar: - type: category label: Guides enumerate: false - link: - id: guides/index - type: doc items: - label: 2024 Migration Guide id: guides/2024-migration-guide @@ -251,8 +260,10 @@ bookSidebar: id: guides/better-error-handling - label: Code Quality Checklist id: guides/code-quality-checklist - - label: Open-sourcing Libraries - id: guides/open-sourcing-libraries + # - label: Open-sourcing Libraries + # id: guides/open-sourcing-libraries + # - label: Creating an NFT Collection + # id: guides/creating-an-nft-collection - type: category label: Appendix enumerate: false From 5a0eb8d0e9110cc5a78f64ca4173d5e22941c48e Mon Sep 17 00:00:00 2001 From: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> Date: Tue, 5 May 2026 10:08:49 -0500 Subject: [PATCH 9/9] Update sidebar.yml --- reference/sidebar.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/reference/sidebar.yml b/reference/sidebar.yml index 9a6792c8..831abc25 100644 --- a/reference/sidebar.yml +++ b/reference/sidebar.yml @@ -75,14 +75,8 @@ referenceSidebar: id: constants - label: 11. Generics id: generics - - type: category - label: 12. Abilities - link: - id: abilities - type: doc - items: - - label: 12.1 Sui Object - id: abilities/object + - label: 12. Abilities + id: abilities - label: 13. Uses and Aliases id: uses - type: category