specra 0.1.13 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.MD +25 -4
- package/README.md +67 -58
- package/config/specra.config.schema.json +16 -0
- package/config/svelte-config.js +63 -0
- package/dist/api-parser.types.d.ts +59 -0
- package/dist/api-parser.types.js +5 -0
- package/dist/api.types.d.ts +137 -0
- package/dist/api.types.js +5 -0
- package/dist/category.d.ts +21 -0
- package/dist/category.js +48 -0
- package/dist/components/ConfigProvider.svelte +13 -0
- package/dist/components/ConfigProvider.svelte.d.ts +31 -0
- package/dist/components/docs/Accordion.svelte +18 -0
- package/dist/components/docs/Accordion.svelte.d.ts +10 -0
- package/dist/components/docs/AccordionItem.svelte +41 -0
- package/dist/components/docs/AccordionItem.svelte.d.ts +10 -0
- package/dist/components/docs/Badge.svelte +28 -0
- package/dist/components/docs/Badge.svelte.d.ts +9 -0
- package/dist/components/docs/Breadcrumb.svelte +80 -0
- package/dist/components/docs/Breadcrumb.svelte.d.ts +8 -0
- package/dist/components/docs/Callout.svelte +96 -0
- package/dist/components/docs/Callout.svelte.d.ts +10 -0
- package/dist/components/docs/Card.svelte +63 -0
- package/dist/components/docs/Card.svelte.d.ts +12 -0
- package/dist/components/docs/CardGrid.svelte +24 -0
- package/dist/components/docs/CardGrid.svelte.d.ts +8 -0
- package/dist/components/docs/CategoryIndex.svelte +110 -0
- package/dist/components/docs/CategoryIndex.svelte.d.ts +29 -0
- package/dist/components/docs/CodeBlock.svelte +172 -0
- package/dist/components/docs/CodeBlock.svelte.d.ts +8 -0
- package/dist/components/docs/Column.svelte +25 -0
- package/dist/components/docs/Column.svelte.d.ts +8 -0
- package/dist/components/docs/Columns.svelte +38 -0
- package/dist/components/docs/Columns.svelte.d.ts +13 -0
- package/dist/components/docs/DevModeBadge.svelte +15 -0
- package/dist/components/docs/DevModeBadge.svelte.d.ts +18 -0
- package/dist/components/docs/DocBadge.svelte +28 -0
- package/dist/components/docs/DocBadge.svelte.d.ts +9 -0
- package/dist/components/docs/DocLayout.svelte +107 -0
- package/dist/components/docs/DocLayout.svelte.d.ts +32 -0
- package/dist/components/docs/DocLoading.svelte +53 -0
- package/dist/components/docs/DocLoading.svelte.d.ts +18 -0
- package/dist/components/docs/DocMetadata.svelte +106 -0
- package/dist/components/docs/DocMetadata.svelte.d.ts +18 -0
- package/dist/components/docs/DocNavigation.svelte +56 -0
- package/dist/components/docs/DocNavigation.svelte.d.ts +12 -0
- package/dist/components/docs/DocTags.svelte +22 -0
- package/dist/components/docs/DocTags.svelte.d.ts +6 -0
- package/dist/components/docs/DraftBadge.svelte +10 -0
- package/dist/components/docs/DraftBadge.svelte.d.ts +18 -0
- package/dist/components/docs/Footer.svelte +72 -0
- package/dist/components/docs/Footer.svelte.d.ts +7 -0
- package/dist/components/docs/Frame.svelte +27 -0
- package/dist/components/docs/Frame.svelte.d.ts +9 -0
- package/dist/components/docs/Header.svelte +123 -0
- package/dist/components/docs/Header.svelte.d.ts +9 -0
- package/dist/components/docs/HeaderWithMenu.svelte +34 -0
- package/dist/components/docs/HeaderWithMenu.svelte.d.ts +17 -0
- package/dist/components/docs/HotReloadIndicator.svelte +44 -0
- package/dist/components/docs/HotReloadIndicator.svelte.d.ts +3 -0
- package/dist/components/docs/Icon.svelte +103 -0
- package/dist/components/docs/Icon.svelte.d.ts +11 -0
- package/dist/components/docs/Image.svelte +88 -0
- package/dist/components/docs/Image.svelte.d.ts +11 -0
- package/dist/components/docs/ImageCard.svelte +91 -0
- package/dist/components/docs/ImageCard.svelte.d.ts +12 -0
- package/dist/components/docs/ImageCardGrid.svelte +25 -0
- package/dist/components/docs/ImageCardGrid.svelte.d.ts +8 -0
- package/dist/components/docs/LayoutProviders.svelte +57 -0
- package/dist/components/docs/LayoutProviders.svelte.d.ts +9 -0
- package/dist/components/docs/Logo.svelte +25 -0
- package/dist/components/docs/Logo.svelte.d.ts +11 -0
- package/dist/components/docs/Math.svelte +54 -0
- package/dist/components/docs/Math.svelte.d.ts +7 -0
- package/dist/components/docs/MdxContent.svelte +41 -0
- package/dist/components/docs/MdxHotReload.svelte +78 -0
- package/dist/components/docs/MdxHotReload.svelte.d.ts +9 -0
- package/dist/components/docs/MdxLayout.svelte +16 -0
- package/dist/components/docs/MdxLayout.svelte.d.ts +6 -0
- package/dist/components/docs/Mermaid.svelte +88 -0
- package/dist/components/docs/Mermaid.svelte.d.ts +7 -0
- package/dist/components/docs/MobileDocLayout.svelte +211 -0
- package/dist/components/docs/MobileDocLayout.svelte.d.ts +35 -0
- package/dist/components/docs/MobileSidebar.svelte +122 -0
- package/dist/components/docs/MobileSidebar.svelte.d.ts +31 -0
- package/dist/components/docs/MobileSidebarWrapper.svelte +122 -0
- package/dist/components/docs/MobileSidebarWrapper.svelte.d.ts +32 -0
- package/dist/components/docs/NotFoundContent.svelte +40 -0
- package/dist/components/docs/NotFoundContent.svelte.d.ts +6 -0
- package/dist/components/docs/SearchHighlight.svelte +116 -0
- package/dist/components/docs/SearchHighlight.svelte.d.ts +3 -0
- package/dist/components/docs/SearchModal.svelte +239 -0
- package/dist/components/docs/SearchModal.svelte.d.ts +9 -0
- package/dist/components/docs/Sidebar.svelte +69 -0
- package/dist/components/docs/Sidebar.svelte.d.ts +31 -0
- package/dist/components/docs/SidebarMenuItems.svelte +344 -0
- package/dist/components/docs/SidebarMenuItems.svelte.d.ts +33 -0
- package/dist/components/docs/SidebarSkeleton.svelte +50 -0
- package/dist/components/docs/SidebarSkeleton.svelte.d.ts +18 -0
- package/dist/components/docs/SiteBanner.svelte +92 -0
- package/dist/components/docs/SiteBanner.svelte.d.ts +7 -0
- package/dist/components/docs/Step.svelte +44 -0
- package/dist/components/docs/Step.svelte.d.ts +8 -0
- package/dist/components/docs/Steps.svelte +15 -0
- package/dist/components/docs/Steps.svelte.d.ts +7 -0
- package/dist/components/docs/Tab.svelte +40 -0
- package/dist/components/docs/Tab.svelte.d.ts +8 -0
- package/dist/components/docs/TabGroups.svelte +183 -0
- package/dist/components/docs/TabGroups.svelte.d.ts +25 -0
- package/dist/components/docs/TableOfContents.svelte +100 -0
- package/dist/components/docs/TableOfContents.svelte.d.ts +9 -0
- package/dist/components/docs/Tabs.svelte +69 -0
- package/dist/components/docs/Tabs.svelte.d.ts +8 -0
- package/dist/components/docs/ThemeToggle.svelte +16 -0
- package/dist/components/docs/ThemeToggle.svelte.d.ts +18 -0
- package/dist/components/docs/Tooltip.svelte +44 -0
- package/dist/components/docs/Tooltip.svelte.d.ts +10 -0
- package/dist/components/docs/VersionSwitcher.svelte +95 -0
- package/dist/components/docs/VersionSwitcher.svelte.d.ts +7 -0
- package/dist/components/docs/Video.svelte +84 -0
- package/dist/components/docs/Video.svelte.d.ts +12 -0
- package/dist/components/docs/api/ApiEndpoint.svelte +61 -0
- package/dist/components/docs/api/ApiEndpoint.svelte.d.ts +11 -0
- package/dist/components/docs/api/ApiParams.svelte +80 -0
- package/dist/components/docs/api/ApiParams.svelte.d.ts +14 -0
- package/dist/components/docs/api/ApiPlayground.svelte +259 -0
- package/dist/components/docs/api/ApiPlayground.svelte.d.ts +16 -0
- package/dist/components/docs/api/ApiReference.svelte +278 -0
- package/dist/components/docs/api/ApiReference.svelte.d.ts +23 -0
- package/dist/components/docs/api/ApiResponse.svelte +66 -0
- package/dist/components/docs/api/ApiResponse.svelte.d.ts +9 -0
- package/dist/components/docs/api/index.d.ts +5 -0
- package/dist/components/docs/api/index.js +5 -0
- package/dist/components/docs/componentTextProps.d.ts +3 -0
- package/dist/components/docs/componentTextProps.js +61 -0
- package/dist/components/docs/index.d.ts +54 -0
- package/dist/components/docs/index.js +56 -0
- package/dist/components/global/VersionNotFound.svelte +48 -0
- package/dist/components/global/VersionNotFound.svelte.d.ts +7 -0
- package/dist/components/global/index.d.ts +1 -0
- package/dist/components/global/index.js +1 -0
- package/dist/components/index.d.ts +6 -822
- package/dist/components/index.js +11 -3854
- package/dist/components/ui/Badge.svelte +48 -0
- package/dist/components/ui/Badge.svelte.d.ts +15 -0
- package/dist/components/ui/Button.svelte +58 -0
- package/dist/components/ui/Button.svelte.d.ts +17 -0
- package/dist/components/ui/Dialog.svelte +16 -0
- package/dist/components/ui/Dialog.svelte.d.ts +9 -0
- package/dist/components/ui/DialogClose.svelte +16 -0
- package/dist/components/ui/DialogClose.svelte.d.ts +9 -0
- package/dist/components/ui/DialogContent.svelte +43 -0
- package/dist/components/ui/DialogContent.svelte.d.ts +10 -0
- package/dist/components/ui/DialogDescription.svelte +21 -0
- package/dist/components/ui/DialogDescription.svelte.d.ts +9 -0
- package/dist/components/ui/DialogFooter.svelte +20 -0
- package/dist/components/ui/DialogFooter.svelte.d.ts +9 -0
- package/dist/components/ui/DialogHeader.svelte +20 -0
- package/dist/components/ui/DialogHeader.svelte.d.ts +9 -0
- package/dist/components/ui/DialogTitle.svelte +21 -0
- package/dist/components/ui/DialogTitle.svelte.d.ts +9 -0
- package/dist/components/ui/Input.svelte +23 -0
- package/dist/components/ui/Input.svelte.d.ts +8 -0
- package/dist/components/ui/Textarea.svelte +19 -0
- package/dist/components/ui/Textarea.svelte.d.ts +7 -0
- package/dist/components/ui/index.d.ts +11 -0
- package/dist/components/ui/index.js +11 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +9 -0
- package/dist/config.schema.json +471 -0
- package/dist/config.server.d.ts +46 -0
- package/dist/config.server.js +149 -0
- package/dist/{mdx-ColN3Cyg.d.mts → config.types.d.ts} +22 -75
- package/dist/config.types.js +39 -0
- package/dist/dev-utils.d.ts +29 -0
- package/dist/dev-utils.js +63 -0
- package/dist/index.d.ts +19 -4
- package/dist/index.js +25 -4861
- package/dist/mdx-cache.d.ts +41 -0
- package/dist/mdx-cache.js +160 -0
- package/dist/mdx-components.js +50 -1931
- package/dist/mdx-security.d.ts +76 -0
- package/dist/mdx-security.js +217 -0
- package/dist/mdx.d.ts +73 -0
- package/dist/mdx.js +1099 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/security.d.ts +22 -47
- package/dist/middleware/security.js +111 -137
- package/dist/parsers/base-parser.d.ts +14 -0
- package/dist/parsers/base-parser.js +1 -0
- package/dist/parsers/index.d.ts +16 -0
- package/dist/parsers/index.js +51 -0
- package/dist/parsers/openapi-parser.d.ts +18 -0
- package/dist/parsers/openapi-parser.js +209 -0
- package/dist/parsers/postman-parser.d.ts +20 -0
- package/dist/parsers/postman-parser.js +260 -0
- package/dist/parsers/specra-parser.d.ts +10 -0
- package/dist/parsers/specra-parser.js +18 -0
- package/dist/redirects.d.ts +12 -0
- package/dist/redirects.js +30 -0
- package/dist/remark-code-meta.d.ts +6 -0
- package/dist/remark-code-meta.js +21 -0
- package/dist/sidebar-utils.d.ts +59 -0
- package/dist/sidebar-utils.js +144 -0
- package/dist/stores/config.d.ts +20 -0
- package/dist/stores/config.js +45 -0
- package/dist/stores/index.d.ts +4 -0
- package/dist/stores/index.js +4 -0
- package/dist/stores/sidebar.d.ts +7 -0
- package/dist/stores/sidebar.js +12 -0
- package/dist/stores/tabs.d.ts +6 -0
- package/dist/stores/tabs.js +41 -0
- package/dist/stores/theme.d.ts +7 -0
- package/dist/stores/theme.js +75 -0
- package/dist/{styles.css → styles/globals.css} +136 -6
- package/dist/toc.d.ts +9 -0
- package/dist/toc.js +15 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +30 -0
- package/package.json +47 -90
- package/dist/app/api/mdx-watch/route.d.mts +0 -10
- package/dist/app/api/mdx-watch/route.d.ts +0 -10
- package/dist/app/api/mdx-watch/route.js +0 -118
- package/dist/app/api/mdx-watch/route.js.map +0 -1
- package/dist/app/api/mdx-watch/route.mjs +0 -91
- package/dist/app/api/mdx-watch/route.mjs.map +0 -1
- package/dist/chunk-6S3EJVEO.mjs +0 -259
- package/dist/chunk-6S3EJVEO.mjs.map +0 -1
- package/dist/chunk-BE7EROIW.mjs +0 -212
- package/dist/chunk-BE7EROIW.mjs.map +0 -1
- package/dist/chunk-CWHRZHZO.mjs +0 -168
- package/dist/chunk-CWHRZHZO.mjs.map +0 -1
- package/dist/chunk-D5VDVYFY.mjs +0 -1325
- package/dist/chunk-D5VDVYFY.mjs.map +0 -1
- package/dist/chunk-WMCO2UX5.mjs +0 -585
- package/dist/chunk-WMCO2UX5.mjs.map +0 -1
- package/dist/chunk-XEMGCPZZ.mjs +0 -475
- package/dist/chunk-XEMGCPZZ.mjs.map +0 -1
- package/dist/components/index.d.mts +0 -822
- package/dist/components/index.js.map +0 -1
- package/dist/components/index.mjs +0 -3741
- package/dist/components/index.mjs.map +0 -1
- package/dist/index.d.mts +0 -4
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -1897
- package/dist/index.mjs.map +0 -1
- package/dist/layouts/index.d.mts +0 -34
- package/dist/layouts/index.d.ts +0 -34
- package/dist/layouts/index.js +0 -453
- package/dist/layouts/index.js.map +0 -1
- package/dist/layouts/index.mjs +0 -173
- package/dist/layouts/index.mjs.map +0 -1
- package/dist/lib/index.d.mts +0 -583
- package/dist/lib/index.d.ts +0 -583
- package/dist/lib/index.js +0 -1595
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/index.mjs +0 -111
- package/dist/lib/index.mjs.map +0 -1
- package/dist/mdx-ColN3Cyg.d.ts +0 -352
- package/dist/mdx-components.d.mts +0 -86
- package/dist/mdx-components.d.ts +0 -86
- package/dist/mdx-components.js.map +0 -1
- package/dist/mdx-components.mjs +0 -206
- package/dist/mdx-components.mjs.map +0 -1
- package/dist/middleware/security.d.mts +0 -82
- package/dist/middleware/security.js.map +0 -1
- package/dist/middleware/security.mjs +0 -84
- package/dist/middleware/security.mjs.map +0 -1
- package/dist/styles.css.map +0 -1
- package/dist/styles.d.mts +0 -2
- package/dist/styles.d.ts +0 -2
- package/dist/styles.js +0 -2
- package/dist/styles.js.map +0 -1
- package/dist/styles.mjs +0 -1
- package/dist/styles.mjs.map +0 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MDX Security Layer
|
|
3
|
+
*
|
|
4
|
+
* Protects against:
|
|
5
|
+
* - XSS via malicious MDX expressions
|
|
6
|
+
* - Path traversal attacks
|
|
7
|
+
* - Dangerous component usage
|
|
8
|
+
* - Cross-domain vulnerabilities
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Sanitize file paths to prevent path traversal attacks
|
|
12
|
+
* Blocks: ../, ..\, absolute paths, encoded traversal attempts
|
|
13
|
+
*/
|
|
14
|
+
export declare function sanitizePath(userPath: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Validate that a file path is within allowed directory
|
|
17
|
+
*/
|
|
18
|
+
export declare function validatePathWithinDirectory(filePath: string, allowedDir: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Scan MDX content for dangerous patterns
|
|
21
|
+
* Returns array of detected issues
|
|
22
|
+
* Note: Skips content inside code blocks to avoid false positives
|
|
23
|
+
*/
|
|
24
|
+
export declare function scanMDXForDangerousPatterns(content: string): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Sanitize MDX content by removing/escaping dangerous patterns
|
|
27
|
+
* This is a defensive measure - ideally content should be rejected if dangerous
|
|
28
|
+
*/
|
|
29
|
+
export declare function sanitizeMDXContent(content: string, strict?: boolean): string;
|
|
30
|
+
/**
|
|
31
|
+
* Content Security Policy configuration
|
|
32
|
+
* Use this in your Next.js middleware or headers config
|
|
33
|
+
*/
|
|
34
|
+
export declare const CSP_DIRECTIVES: {
|
|
35
|
+
readonly "default-src": readonly ["'self'"];
|
|
36
|
+
readonly "script-src": readonly ["'self'", "'unsafe-inline'", "'unsafe-eval'"];
|
|
37
|
+
readonly "style-src": readonly ["'self'", "'unsafe-inline'"];
|
|
38
|
+
readonly "img-src": readonly ["'self'", "data:", "https:"];
|
|
39
|
+
readonly "font-src": readonly ["'self'", "data:"];
|
|
40
|
+
readonly "connect-src": readonly ["'self'"];
|
|
41
|
+
readonly "frame-src": readonly ["'self'"];
|
|
42
|
+
readonly "object-src": readonly ["'none'"];
|
|
43
|
+
readonly "base-uri": readonly ["'self'"];
|
|
44
|
+
readonly "form-action": readonly ["'self'"];
|
|
45
|
+
readonly "frame-ancestors": readonly ["'self'"];
|
|
46
|
+
readonly "upgrade-insecure-requests": readonly [];
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Generate CSP header value from directives
|
|
50
|
+
*/
|
|
51
|
+
export declare function generateCSPHeader(customDirectives?: Partial<typeof CSP_DIRECTIVES>, production?: boolean): string;
|
|
52
|
+
/**
|
|
53
|
+
* Allowlist of safe MDX components
|
|
54
|
+
* Only these components can be used in MDX files
|
|
55
|
+
*/
|
|
56
|
+
export declare const SAFE_MDX_COMPONENTS: Set<string>;
|
|
57
|
+
/**
|
|
58
|
+
* Validate component usage in MDX
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateMDXComponents(content: string): {
|
|
61
|
+
valid: boolean;
|
|
62
|
+
issues: string[];
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Comprehensive MDX security check
|
|
66
|
+
* Use this before processing MDX content
|
|
67
|
+
*/
|
|
68
|
+
export declare function validateMDXSecurity(content: string, options?: {
|
|
69
|
+
strictMode?: boolean;
|
|
70
|
+
allowCustomComponents?: boolean;
|
|
71
|
+
blockDangerousPatterns?: boolean;
|
|
72
|
+
}): {
|
|
73
|
+
valid: boolean;
|
|
74
|
+
issues: string[];
|
|
75
|
+
sanitized?: string;
|
|
76
|
+
};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MDX Security Layer
|
|
3
|
+
*
|
|
4
|
+
* Protects against:
|
|
5
|
+
* - XSS via malicious MDX expressions
|
|
6
|
+
* - Path traversal attacks
|
|
7
|
+
* - Dangerous component usage
|
|
8
|
+
* - Cross-domain vulnerabilities
|
|
9
|
+
*/
|
|
10
|
+
import path from "path";
|
|
11
|
+
/**
|
|
12
|
+
* Sanitize file paths to prevent path traversal attacks
|
|
13
|
+
* Blocks: ../, ..\, absolute paths, encoded traversal attempts
|
|
14
|
+
*/
|
|
15
|
+
export function sanitizePath(userPath) {
|
|
16
|
+
// Decode URI components to catch encoded traversal attempts
|
|
17
|
+
const decoded = decodeURIComponent(userPath);
|
|
18
|
+
// Block path traversal patterns
|
|
19
|
+
if (decoded.includes("../") ||
|
|
20
|
+
decoded.includes("..\\") ||
|
|
21
|
+
decoded.includes("%2e%2e") ||
|
|
22
|
+
decoded.includes("%252e%252e") ||
|
|
23
|
+
path.isAbsolute(decoded)) {
|
|
24
|
+
throw new Error("Path traversal detected");
|
|
25
|
+
}
|
|
26
|
+
// Normalize and validate the path
|
|
27
|
+
const normalized = path.normalize(decoded).replace(/\\/g, "/");
|
|
28
|
+
// Ensure path doesn't escape after normalization
|
|
29
|
+
if (normalized.startsWith("..") || normalized.includes("/../")) {
|
|
30
|
+
throw new Error("Invalid path detected");
|
|
31
|
+
}
|
|
32
|
+
return normalized;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validate that a file path is within allowed directory
|
|
36
|
+
*/
|
|
37
|
+
export function validatePathWithinDirectory(filePath, allowedDir) {
|
|
38
|
+
const resolvedPath = path.resolve(allowedDir, filePath);
|
|
39
|
+
const resolvedDir = path.resolve(allowedDir);
|
|
40
|
+
return resolvedPath.startsWith(resolvedDir + path.sep) || resolvedPath === resolvedDir;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Dangerous MDX patterns that should be blocked
|
|
44
|
+
* These patterns can execute arbitrary code during SSR
|
|
45
|
+
*/
|
|
46
|
+
const DANGEROUS_PATTERNS = [
|
|
47
|
+
// JavaScript execution
|
|
48
|
+
/eval\s*\(/gi,
|
|
49
|
+
/Function\s*\(/gi,
|
|
50
|
+
/import\s*\(/gi,
|
|
51
|
+
/require\s*\(/gi,
|
|
52
|
+
// File system access
|
|
53
|
+
/fs\.[a-z]+/gi,
|
|
54
|
+
/readFile/gi,
|
|
55
|
+
/writeFile/gi,
|
|
56
|
+
/process\.env/gi,
|
|
57
|
+
// Network requests during SSR (legitimate client-side usage should use components)
|
|
58
|
+
/fetch\s*\(/gi,
|
|
59
|
+
// Dangerous Node.js modules
|
|
60
|
+
/child_process/gi,
|
|
61
|
+
/exec\s*\(/gi,
|
|
62
|
+
/spawn\s*\(/gi,
|
|
63
|
+
// Script tag injection
|
|
64
|
+
/<script[>\s]/gi,
|
|
65
|
+
/javascript:/gi,
|
|
66
|
+
/\bon(abort|blur|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|dblclick|drag|dragend|dragenter|dragleave|dragover|dragstart|drop|durationchange|emptied|ended|error|focus|input|invalid|keydown|keypress|keyup|load|loadeddata|loadedmetadata|loadstart|mousedown|mouseenter|mouseleave|mousemove|mouseout|mouseover|mouseup|mousewheel|pause|play|playing|progress|ratechange|reset|resize|scroll|seeked|seeking|select|show|stalled|submit|suspend|timeupdate|toggle|volumechange|waiting|wheel)\s*=/gi, // onclick, onerror, onload, etc.
|
|
67
|
+
];
|
|
68
|
+
/**
|
|
69
|
+
* Remove code blocks from content to avoid scanning code examples
|
|
70
|
+
* This prevents false positives from documentation code examples
|
|
71
|
+
*/
|
|
72
|
+
function removeCodeBlocks(content) {
|
|
73
|
+
// Remove fenced code blocks (```...```)
|
|
74
|
+
let withoutCodeBlocks = content.replace(/```[\s\S]*?```/g, '');
|
|
75
|
+
// Remove inline code (`...`)
|
|
76
|
+
withoutCodeBlocks = withoutCodeBlocks.replace(/`[^`]*`/g, '');
|
|
77
|
+
return withoutCodeBlocks;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Scan MDX content for dangerous patterns
|
|
81
|
+
* Returns array of detected issues
|
|
82
|
+
* Note: Skips content inside code blocks to avoid false positives
|
|
83
|
+
*/
|
|
84
|
+
export function scanMDXForDangerousPatterns(content) {
|
|
85
|
+
const issues = [];
|
|
86
|
+
// Remove code blocks before scanning to avoid false positives
|
|
87
|
+
const contentWithoutCode = removeCodeBlocks(content);
|
|
88
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
89
|
+
const matches = contentWithoutCode.match(pattern);
|
|
90
|
+
if (matches) {
|
|
91
|
+
issues.push(`Dangerous pattern detected: ${pattern.source}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return issues;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Sanitize MDX content by removing/escaping dangerous patterns
|
|
98
|
+
* This is a defensive measure - ideally content should be rejected if dangerous
|
|
99
|
+
*/
|
|
100
|
+
export function sanitizeMDXContent(content, strict = false) {
|
|
101
|
+
if (strict) {
|
|
102
|
+
const issues = scanMDXForDangerousPatterns(content);
|
|
103
|
+
if (issues.length > 0) {
|
|
104
|
+
throw new Error(`MDX content contains dangerous patterns: ${issues.join(", ")}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Remove inline script tags
|
|
108
|
+
let sanitized = content.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "");
|
|
109
|
+
// Remove event handlers from HTML tags
|
|
110
|
+
sanitized = sanitized.replace(/\s+on\w+\s*=\s*["'][^"']*["']/gi, "");
|
|
111
|
+
// Remove javascript: protocol
|
|
112
|
+
sanitized = sanitized.replace(/javascript:/gi, "");
|
|
113
|
+
return sanitized;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Content Security Policy configuration
|
|
117
|
+
* Use this in your Next.js middleware or headers config
|
|
118
|
+
*/
|
|
119
|
+
export const CSP_DIRECTIVES = {
|
|
120
|
+
"default-src": ["'self'"],
|
|
121
|
+
"script-src": [
|
|
122
|
+
"'self'",
|
|
123
|
+
"'unsafe-inline'", // Required for Next.js
|
|
124
|
+
"'unsafe-eval'", // Required for dev mode - remove in production
|
|
125
|
+
],
|
|
126
|
+
"style-src": ["'self'", "'unsafe-inline'"], // Required for styled-components/emotion
|
|
127
|
+
"img-src": ["'self'", "data:", "https:"],
|
|
128
|
+
"font-src": ["'self'", "data:"],
|
|
129
|
+
"connect-src": ["'self'"],
|
|
130
|
+
"frame-src": ["'self'"],
|
|
131
|
+
"object-src": ["'none'"],
|
|
132
|
+
"base-uri": ["'self'"],
|
|
133
|
+
"form-action": ["'self'"],
|
|
134
|
+
"frame-ancestors": ["'self'"],
|
|
135
|
+
"upgrade-insecure-requests": [],
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Generate CSP header value from directives
|
|
139
|
+
*/
|
|
140
|
+
export function generateCSPHeader(customDirectives, production = true) {
|
|
141
|
+
const directives = { ...CSP_DIRECTIVES, ...customDirectives };
|
|
142
|
+
// Remove unsafe-eval in production
|
|
143
|
+
if (production && directives["script-src"]) {
|
|
144
|
+
directives["script-src"] = directives["script-src"].filter((src) => src !== "'unsafe-eval'");
|
|
145
|
+
}
|
|
146
|
+
return Object.entries(directives)
|
|
147
|
+
.map(([key, values]) => `${key} ${values.join(" ")}`)
|
|
148
|
+
.join("; ");
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Allowlist of safe MDX components
|
|
152
|
+
* Only these components can be used in MDX files
|
|
153
|
+
*/
|
|
154
|
+
export const SAFE_MDX_COMPONENTS = new Set([
|
|
155
|
+
// Standard HTML elements (automatically allowed by MDX)
|
|
156
|
+
"h1", "h2", "h3", "h4", "h5", "h6",
|
|
157
|
+
"p", "a", "ul", "ol", "li", "code", "pre",
|
|
158
|
+
"blockquote", "table", "thead", "tbody", "tr", "th", "td",
|
|
159
|
+
"img", "video", "audio", "br", "hr", "strong", "em",
|
|
160
|
+
// Custom safe components
|
|
161
|
+
"Callout", "CodeBlock", "Accordion", "AccordionItem",
|
|
162
|
+
"Tabs", "Tab", "Image", "Video", "Card", "CardGrid",
|
|
163
|
+
"ImageCard", "ImageCardGrid", "Steps", "Step",
|
|
164
|
+
"Icon", "Mermaid", "Math", "Columns", "Column",
|
|
165
|
+
"Badge", "Tooltip", "Frame",
|
|
166
|
+
"ApiEndpoint", "ApiParams", "ApiResponse", "ApiPlayground", "ApiReference",
|
|
167
|
+
]);
|
|
168
|
+
/**
|
|
169
|
+
* Validate component usage in MDX
|
|
170
|
+
*/
|
|
171
|
+
export function validateMDXComponents(content) {
|
|
172
|
+
const issues = [];
|
|
173
|
+
// Find all JSX-like component usage
|
|
174
|
+
const componentRegex = /<([A-Z][a-zA-Z0-9]*)/g;
|
|
175
|
+
let match;
|
|
176
|
+
while ((match = componentRegex.exec(content)) !== null) {
|
|
177
|
+
const componentName = match[1];
|
|
178
|
+
if (!SAFE_MDX_COMPONENTS.has(componentName)) {
|
|
179
|
+
issues.push(`Unsafe component detected: ${componentName}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
valid: issues.length === 0,
|
|
184
|
+
issues,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Comprehensive MDX security check
|
|
189
|
+
* Use this before processing MDX content
|
|
190
|
+
*/
|
|
191
|
+
export function validateMDXSecurity(content, options = {}) {
|
|
192
|
+
const { strictMode = false, allowCustomComponents = true, blockDangerousPatterns = true, } = options;
|
|
193
|
+
const issues = [];
|
|
194
|
+
// Check for dangerous patterns
|
|
195
|
+
if (blockDangerousPatterns) {
|
|
196
|
+
const patternIssues = scanMDXForDangerousPatterns(content);
|
|
197
|
+
issues.push(...patternIssues);
|
|
198
|
+
}
|
|
199
|
+
// Validate components
|
|
200
|
+
if (!allowCustomComponents) {
|
|
201
|
+
const componentValidation = validateMDXComponents(content);
|
|
202
|
+
if (!componentValidation.valid) {
|
|
203
|
+
issues.push(...componentValidation.issues);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// In strict mode, reject any issues
|
|
207
|
+
if (strictMode && issues.length > 0) {
|
|
208
|
+
return { valid: false, issues };
|
|
209
|
+
}
|
|
210
|
+
// Otherwise, sanitize and warn
|
|
211
|
+
const sanitized = sanitizeMDXContent(content, false);
|
|
212
|
+
return {
|
|
213
|
+
valid: true,
|
|
214
|
+
issues,
|
|
215
|
+
sanitized,
|
|
216
|
+
};
|
|
217
|
+
}
|
package/dist/mdx.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { I18nConfig } from "./config.types";
|
|
2
|
+
/**
|
|
3
|
+
* Structured node type for MDX content rendering.
|
|
4
|
+
* Used to pass a JSON-serializable tree from server to client,
|
|
5
|
+
* allowing the client-side recursive renderer to instantiate
|
|
6
|
+
* real Svelte components for custom tags.
|
|
7
|
+
*/
|
|
8
|
+
export interface MdxNode {
|
|
9
|
+
type: 'html' | 'component';
|
|
10
|
+
content?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
props?: Record<string, any>;
|
|
13
|
+
children?: MdxNode[];
|
|
14
|
+
}
|
|
15
|
+
export interface DocMeta {
|
|
16
|
+
title: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
slug?: string;
|
|
19
|
+
section?: string;
|
|
20
|
+
group?: string;
|
|
21
|
+
sidebar?: string;
|
|
22
|
+
order?: number;
|
|
23
|
+
sidebar_position?: number;
|
|
24
|
+
content?: string;
|
|
25
|
+
last_updated?: string;
|
|
26
|
+
draft?: boolean;
|
|
27
|
+
authors?: Array<{
|
|
28
|
+
id: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
}>;
|
|
31
|
+
tags?: string[];
|
|
32
|
+
redirect_from?: string[];
|
|
33
|
+
reading_time?: number;
|
|
34
|
+
word_count?: number;
|
|
35
|
+
icon?: string;
|
|
36
|
+
tab_group?: string;
|
|
37
|
+
locale?: string;
|
|
38
|
+
protected?: boolean;
|
|
39
|
+
isProtected?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface Doc {
|
|
42
|
+
slug: string;
|
|
43
|
+
filePath: string;
|
|
44
|
+
title: string;
|
|
45
|
+
meta: DocMeta;
|
|
46
|
+
content: string;
|
|
47
|
+
contentNodes?: MdxNode[];
|
|
48
|
+
categoryLabel?: string;
|
|
49
|
+
categoryPosition?: number;
|
|
50
|
+
categoryCollapsible?: boolean;
|
|
51
|
+
categoryCollapsed?: boolean;
|
|
52
|
+
categoryIcon?: string;
|
|
53
|
+
categoryTabGroup?: string;
|
|
54
|
+
locale?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface TocItem {
|
|
57
|
+
id: string;
|
|
58
|
+
title: string;
|
|
59
|
+
level: number;
|
|
60
|
+
}
|
|
61
|
+
export declare function getVersions(): string[];
|
|
62
|
+
export declare function getI18nConfig(): I18nConfig | null;
|
|
63
|
+
export declare function getDocBySlug(slug: string, version?: string, locale?: string): Promise<Doc | null>;
|
|
64
|
+
export declare function getAllDocs(version?: string, locale?: string): Doc[];
|
|
65
|
+
export declare function getAdjacentDocs(currentSlug: string, allDocs: Doc[]): {
|
|
66
|
+
previous?: Doc;
|
|
67
|
+
next?: Doc;
|
|
68
|
+
};
|
|
69
|
+
export declare function extractTableOfContents(content: string): TocItem[];
|
|
70
|
+
/**
|
|
71
|
+
* Check if a slug represents a category (has child documents)
|
|
72
|
+
*/
|
|
73
|
+
export declare function isCategoryPage(slug: string, allDocs: Doc[]): boolean;
|